diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 440a9d50c5..a1caad9afe 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -48,6 +48,7 @@ jobs: if: startsWith(matrix.os, 'windows-') run: npm config set msvs_version 2015 - run: npm ci + - run: npm run tsc - run: npm test env: FORCE_COLOR: 1 diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 59c36d3940..6408779930 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -22,6 +22,7 @@ jobs: with: node-version: ${{ matrix.node }} - run: npm ci + - run: npm run tsc - run: npm test env: FORCE_COLOR: 1 @@ -66,6 +67,7 @@ jobs: if: startsWith(matrix.os, 'windows-') run: npm config set msvs_version 2015 - run: npm ci + - run: npm run tsc - run: npm test env: FORCE_COLOR: 1 diff --git a/package.json b/package.json index 9b0c2fa1e6..8bdacc3d4e 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "start": "lerna exec --loglevel=silent --scope ganache -- npm run start --silent -- ", "test": "lerna exec -- npm run test", "tsc": "ttsc --build src", - "tsc.clean": "npx lerna exec -- npx shx rm -rf lib dist" + "tsc.clean": "npx lerna exec -- npx shx rm -rf lib dist", + "tsc.filecoin.declarations": "lerna run tsc.declarations --scope @ganache/filecoin && pretty-quick src/chains/filecoin/types" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "1.0.1", diff --git a/src/chains/ethereum/ethereum/src/api.ts b/src/chains/ethereum/ethereum/src/api.ts index f7e3508d8f..8bdcbd34ed 100644 --- a/src/chains/ethereum/ethereum/src/api.ts +++ b/src/chains/ethereum/ethereum/src/api.ts @@ -27,7 +27,7 @@ import { hashPersonalMessage } from "ethereumjs-util"; import { TypedData as NotTypedData, signTypedData_v4 } from "eth-sig-util"; -import { EthereumInternalOptions } from "@ganache/ethereum-options"; +import { EthereumInternalOptions, Hardfork } from "@ganache/ethereum-options"; import { types, Data, Quantity, PromiEvent, utils } from "@ganache/utils"; import Blockchain, { TransactionTraceOptions } from "./blockchain"; import Wallet from "./wallet"; @@ -44,7 +44,6 @@ import { parseFilterDetails, parseFilterRange } from "./helpers/filter-parsing"; -import { Hardfork } from "@ganache/ethereum-options"; // Read in the current ganache version from core's package.json const { version } = $INLINE_JSON("../../../../packages/ganache/package.json"); diff --git a/src/chains/ethereum/ethereum/src/connector.ts b/src/chains/ethereum/ethereum/src/connector.ts index 6d7ffe14e6..7c46536931 100644 --- a/src/chains/ethereum/ethereum/src/connector.ts +++ b/src/chains/ethereum/ethereum/src/connector.ts @@ -6,10 +6,10 @@ import { RecognizedString, WebSocket, HttpRequest } from "uWebSockets.js"; import { CodedError, ErrorCodes } from "@ganache/ethereum-utils"; import { EthereumProviderOptions, - EthereumLegacyOptions + EthereumLegacyProviderOptions } from "@ganache/ethereum-options"; -export type ProviderOptions = EthereumProviderOptions | EthereumLegacyOptions; +type ProviderOptions = EthereumProviderOptions | EthereumLegacyProviderOptions; export type Provider = EthereumProvider; export const Provider = EthereumProvider; diff --git a/src/chains/ethereum/ethereum/src/provider.ts b/src/chains/ethereum/ethereum/src/provider.ts index 08df9664e8..4261490007 100644 --- a/src/chains/ethereum/ethereum/src/provider.ts +++ b/src/chains/ethereum/ethereum/src/provider.ts @@ -5,7 +5,7 @@ import { EthereumProviderOptions, EthereumInternalOptions, EthereumOptionsConfig, - EthereumLegacyOptions + EthereumLegacyProviderOptions } from "@ganache/ethereum-options"; import cloneDeep from "lodash.clonedeep"; import { PromiEvent, types, utils } from "@ganache/utils"; @@ -43,7 +43,7 @@ export default class EthereumProvider #wallet: Wallet; constructor( - options: EthereumProviderOptions | EthereumLegacyOptions = {}, + options: EthereumProviderOptions | EthereumLegacyProviderOptions = {}, executor: utils.Executor ) { super(); diff --git a/src/chains/ethereum/ethereum/tests/api/debug/debug.test.ts b/src/chains/ethereum/ethereum/tests/api/debug/debug.test.ts index 221d5a8cef..759cf87a47 100644 --- a/src/chains/ethereum/ethereum/tests/api/debug/debug.test.ts +++ b/src/chains/ethereum/ethereum/tests/api/debug/debug.test.ts @@ -311,6 +311,6 @@ describe("api", () => { } assert.strictEqual(countMap.size, expectedObjectsInFinalTrace); - }); + }).timeout(5000); }); }); diff --git a/src/chains/ethereum/options/src/index.ts b/src/chains/ethereum/options/src/index.ts index f2971bd0f4..d499967d23 100644 --- a/src/chains/ethereum/options/src/index.ts +++ b/src/chains/ethereum/options/src/index.ts @@ -6,7 +6,6 @@ import { WalletConfig, WalletOptions } from "./wallet-options"; import { Base, Defaults, - Definitions, ExternalConfig, InternalConfig, Legacy, @@ -18,7 +17,7 @@ import { } from "@ganache/options"; import { UnionToIntersection } from "./helper-types"; -export type EthereumOptions = { +type EthereumConfig = { chain: ChainConfig; database: DatabaseConfig; logging: LoggingConfig; @@ -26,6 +25,14 @@ export type EthereumOptions = { wallet: WalletConfig; }; +export const EthereumDefaults: Defaults = { + chain: ChainOptions, + database: DatabaseOptions, + logging: LoggingOptions, + miner: MinerOptions, + wallet: WalletOptions +}; + type MakeLegacyOptions = UnionToIntersection< { [K in OptionName]: K extends LegacyOptions @@ -34,7 +41,7 @@ type MakeLegacyOptions = UnionToIntersection< }[keyof Options] >; -export type EthereumLegacyOptions = Partial< +export type EthereumLegacyProviderOptions = Partial< MakeLegacyOptions & MakeLegacyOptions & MakeLegacyOptions & @@ -44,27 +51,15 @@ export type EthereumLegacyOptions = Partial< export type EthereumProviderOptions = Partial< { - [K in keyof EthereumOptions]: ExternalConfig; + [K in keyof EthereumConfig]: ExternalConfig; } >; export type EthereumInternalOptions = { - [K in keyof EthereumOptions]: InternalConfig; -}; - -export type EthereumDefaults = { - [K in keyof EthereumOptions]: Definitions; -}; - -export const ethereumDefaults: Defaults = { - chain: ChainOptions, - database: DatabaseOptions, - logging: LoggingOptions, - miner: MinerOptions, - wallet: WalletOptions + [K in keyof EthereumConfig]: InternalConfig; }; -export const EthereumOptionsConfig = new OptionsConfig(ethereumDefaults); +export const EthereumOptionsConfig = new OptionsConfig(EthereumDefaults); export * from "./chain-options"; export * from "./database-options"; diff --git a/src/chains/ethereum/options/src/miner-options.ts b/src/chains/ethereum/options/src/miner-options.ts index 2463dee36c..f5b4f4e388 100644 --- a/src/chains/ethereum/options/src/miner-options.ts +++ b/src/chains/ethereum/options/src/miner-options.ts @@ -151,7 +151,13 @@ export type MinerConfig = { export const MinerOptions: Definitions = { blockTime: { - normalize, + normalize: rawInput => { + if (rawInput < 0) { + throw new Error("miner.blockTime must be 0 or a positive number."); + } + + return rawInput; + }, cliDescription: 'Sets the `blockTime` in seconds for automatic mining. A blockTime of `0` enables "instamine mode", where new executable transactions will be mined instantly.', default: () => 0, diff --git a/src/chains/filecoin/filecoin/npm-shrinkwrap.json b/src/chains/filecoin/filecoin/npm-shrinkwrap.json index 326c587e49..84cdec10e7 100644 --- a/src/chains/filecoin/filecoin/npm-shrinkwrap.json +++ b/src/chains/filecoin/filecoin/npm-shrinkwrap.json @@ -10,33 +10,34 @@ "integrity": "sha512-HazVq9zwTVwGmqdwYzu7WyQ6FQVZ7SwET0KKQuKm55jD0IfUpZgN0OPIiZG3zV1iSrVYcN0bdwLRXI/VNCYsUA==" }, "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", "dev": true, "requires": { "@babel/highlight": "^7.10.4" } }, "@babel/core": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", - "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.10.4.tgz", + "integrity": "sha512-3A0tS0HWpy4XujGc7QtOIHTeNwUgWaZc/WuS5YQrfhU67jnVmsD6OGPc1AKHH0LJHQICGncy3+YUjIhVlfDdcA==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.12.10", - "@babel/helper-module-transforms": "^7.12.1", - "@babel/helpers": "^7.12.5", - "@babel/parser": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.10", + "@babel/generator": "^7.10.4", + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helpers": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", "json5": "^2.1.2", - "lodash": "^4.17.19", + "lodash": "^4.17.13", + "resolve": "^1.3.2", "semver": "^5.4.1", "source-map": "^0.5.0" }, @@ -50,125 +51,125 @@ } }, "@babel/generator": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", - "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.4.tgz", + "integrity": "sha512-toLIHUIAgcQygFZRAQcsLQV3CBuX6yOIru1kJk/qqqvcRmZrYe6WavZTSG+bB8MxhnL9YPf+pKQfuiP161q7ng==", "dev": true, "requires": { - "@babel/types": "^7.12.11", + "@babel/types": "^7.10.4", "jsesc": "^2.5.1", + "lodash": "^4.17.13", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", - "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.12.10", - "@babel/template": "^7.12.7", - "@babel/types": "^7.12.11" + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-get-function-arity": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", - "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.10.4" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", - "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.4.tgz", + "integrity": "sha512-m5j85pK/KZhuSdM/8cHUABQTAslV47OjfIB9Cc7P+PvlAoBzdb79BGNfw8RhT5Mq3p+xGd0ZfAKixbrUZx0C7A==", "dev": true, "requires": { - "@babel/types": "^7.12.7" + "@babel/types": "^7.10.4" } }, "@babel/helper-module-imports": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", - "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", + "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", "dev": true, "requires": { - "@babel/types": "^7.12.5" + "@babel/types": "^7.10.4" } }, "@babel/helper-module-transforms": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", - "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.4.tgz", + "integrity": "sha512-Er2FQX0oa3nV7eM1o0tNCTx7izmQtwAQsIiaLRWtavAAEcskb0XJ5OjJbVrYXWOTr8om921Scabn4/tzlx7j1Q==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.1", - "@babel/helper-replace-supers": "^7.12.1", - "@babel/helper-simple-access": "^7.12.1", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/helper-validator-identifier": "^7.10.4", + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.1", - "@babel/types": "^7.12.1", - "lodash": "^4.17.19" + "@babel/types": "^7.10.4", + "lodash": "^4.17.13" } }, "@babel/helper-optimise-call-expression": { - "version": "7.12.10", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", - "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", "dev": true, "requires": { - "@babel/types": "^7.12.10" + "@babel/types": "^7.10.4" } }, "@babel/helper-replace-supers": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", - "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", + "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.12.7", - "@babel/helper-optimise-call-expression": "^7.12.10", - "@babel/traverse": "^7.12.10", - "@babel/types": "^7.12.11" + "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-simple-access": { - "version": "7.12.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", - "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", + "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", "dev": true, "requires": { - "@babel/types": "^7.12.1" + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", - "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", + "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", "dev": true, "requires": { - "@babel/types": "^7.12.11" + "@babel/types": "^7.10.4" } }, "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", "dev": true }, "@babel/helpers": { - "version": "7.12.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", - "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", + "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", "dev": true, "requires": { "@babel/template": "^7.10.4", - "@babel/traverse": "^7.12.5", - "@babel/types": "^7.12.5" + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/highlight": { @@ -183,47 +184,56 @@ } }, "@babel/parser": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.4.tgz", + "integrity": "sha512-8jHII4hf+YVDsskTF6WuMB3X4Eh+PsUkC2ljq22so5rHvH+T8BzyL94VOdyFLNR8tBSVXOTbNHOKpR4TfRxVtA==", "dev": true }, + "@babel/runtime-corejs3": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.10.4.tgz", + "integrity": "sha512-BFlgP2SoLO9HJX9WBwN67gHWMBhDX/eDz64Jajd6mR/UAUzqrNMm99d4qHnVaKscAElZoFiPv+JpR/Siud5lXw==", + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, "@babel/template": { - "version": "7.12.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", - "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", "dev": true, "requires": { "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.12.7", - "@babel/types": "^7.12.7" + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" } }, "@babel/traverse": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", - "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.4.tgz", + "integrity": "sha512-aSy7p5THgSYm4YyxNGz6jZpXf+Ok40QF3aA2LyIONkDHpAcJzDUqlCKXv6peqYUs2gmic849C/t2HKw2a2K20Q==", "dev": true, "requires": { - "@babel/code-frame": "^7.12.11", - "@babel/generator": "^7.12.11", - "@babel/helper-function-name": "^7.12.11", - "@babel/helper-split-export-declaration": "^7.12.11", - "@babel/parser": "^7.12.11", - "@babel/types": "^7.12.12", + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.10.4", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4", "debug": "^4.1.0", "globals": "^11.1.0", - "lodash": "^4.17.19" + "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.12.12", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", - "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.4.tgz", + "integrity": "sha512-UTCFOxC3FsFHb7lkRMVvgLzaRVamXuAs2Tz4wajva4WxtVY82eZeaUBtC2Zt95FU9TiznuC0Zk35tsim8jeVpg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.13", "to-fast-properties": "^2.0.0" } }, @@ -240,9 +250,9 @@ "dev": true }, "@filecoin-shipyard/lotus-client-schema": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/@filecoin-shipyard/lotus-client-schema/-/lotus-client-schema-0.0.12.tgz", - "integrity": "sha512-wwqarwcUtqpy1pKq4o0jWM5ppbYxsnMZ5uQZ+WJ8dJmNiJuVTz6GjCtWNaCa+4kw2GgfkItj2BIjzweL7/5QNA==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@filecoin-shipyard/lotus-client-schema/-/lotus-client-schema-2.0.0.tgz", + "integrity": "sha512-3XIoyCPr7FyK9rbI3LIKAB755TlpQ0ankFs7uDpU8JXA38gZK7YGc+zcXMLiyIzknbhOmlfGPJ1jpcm3n9Y/0A==" }, "@hapi/accept": { "version": "3.2.4", @@ -731,6 +741,15 @@ "@sinonjs/commons": "^1.7.0" } }, + "@sinonjs/formatio": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-5.0.1.tgz", + "integrity": "sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ==", + "requires": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^5.0.2" + } + }, "@sinonjs/samsam": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.0.tgz", @@ -775,20 +794,46 @@ "@types/node": "*" } }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, "@types/debug": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==" }, + "@types/deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg==", + "dev": true + }, + "@types/lodash": { + "version": "4.14.167", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.167.tgz", + "integrity": "sha512-w7tQPjARrvdeBkX/Rwg95S592JwxqOjmms3zWQ0XZgSyxSLdzWaYH3vErBhdVS/lRBX7F8aBYcYJYTr5TMGOzw==", + "dev": true + }, + "@types/lodash.clonedeep": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.6.tgz", + "integrity": "sha512-cE1jYr2dEg1wBImvXlNtp0xDoS79rfEdGozQVgliDZj1uERH4k+rmEMTudP9b4VQ8O6nRb5gPqft0QzEQGMQgA==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, "@types/long": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" }, "@types/node": { - "version": "14.14.20", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz", - "integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==" + "version": "14.14.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.14.tgz", + "integrity": "sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ==" }, "@types/pbkdf2": { "version": "3.1.0", @@ -806,6 +851,21 @@ "@types/node": "*" } }, + "@types/seedrandom": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.28.tgz", + "integrity": "sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA==", + "dev": true + }, + "@types/ws": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.0.tgz", + "integrity": "sha512-Y29uQ3Uy+58bZrFLhX36hcI3Np37nqWE7ky5tjiDoy1GDZnIwVxS0CgF+s+1bXMzjKBFy+fqaRfb708iNzdinw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -851,9 +911,9 @@ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" }, "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -913,11 +973,11 @@ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "requires": { - "color-convert": "^2.0.1" + "color-convert": "^1.9.0" } }, "any-signal": { @@ -958,13 +1018,6 @@ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "requires": { "sprintf-js": "~1.0.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - } } }, "args": { @@ -996,36 +1049,14 @@ "integrity": "sha1-fqSIKjVrS8pfVF4LblLq9tlxVXo=" }, "array.prototype.map": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.3.tgz", - "integrity": "sha512-nNcb30v0wfDyIe26Yif3PcV1JXQp4zEeEfupG7L4SRjnD6HLbO5b2a7eVSba53bOx4YCHYMBHt+Fp4vYstneRA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.2.tgz", + "integrity": "sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==", "requires": { - "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", + "es-abstract": "^1.17.0-next.1", "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - } + "is-string": "^1.0.4" } }, "arraybuffer.slice": { @@ -1064,6 +1095,11 @@ "lodash": "^4.17.14" } }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, "async.nexttick": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/async.nexttick/-/async.nexttick-0.5.2.tgz", @@ -1165,6 +1201,14 @@ "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "requires": { + "callsite": "1.0.0" + } + }, "bignumber.js": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", @@ -1311,6 +1355,14 @@ "widest-line": "^3.1.0" }, "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, "chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -1320,6 +1372,19 @@ "supports-color": "^7.1.0" } }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1424,6 +1489,13 @@ "parse-asn1": "^5.1.5", "readable-stream": "^3.6.0", "safe-buffer": "^5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "bs58": { @@ -1528,6 +1600,11 @@ "get-intrinsic": "^1.0.0" } }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -1559,29 +1636,6 @@ "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - } } }, "check-error": { @@ -1653,6 +1707,17 @@ "multibase": "^1.0.0", "multicodec": "^1.0.1", "multihashes": "^1.0.1" + }, + "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } } }, "cipher-base": { @@ -1698,17 +1763,17 @@ } }, "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.3" } }, "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "combined-stream": { "version": "1.0.8", @@ -1735,9 +1800,9 @@ "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" }, "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" }, "component-inherit": { "version": "0.0.3", @@ -1769,20 +1834,17 @@ "dev": true, "requires": { "safe-buffer": "~5.1.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } } }, "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "core-js-pure": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", + "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==" }, "core-util-is": { "version": "1.0.2", @@ -1943,11 +2005,11 @@ "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==" }, "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { - "ms": "2.1.2" + "ms": "^2.1.1" } }, "decamelize": { @@ -2195,32 +2257,22 @@ } }, "engine.io": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz", - "integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz", + "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==", "requires": { "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "0.3.1", "debug": "~4.1.0", "engine.io-parser": "~2.2.0", - "ws": "~7.4.2" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - } + "ws": "^7.1.2" } }, "engine.io-client": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.0.tgz", - "integrity": "sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz", + "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==", "requires": { "component-emitter": "~1.3.0", "component-inherit": "0.0.3", @@ -2230,11 +2282,16 @@ "indexof": "0.0.1", "parseqs": "0.0.6", "parseuri": "0.0.6", - "ws": "~7.4.2", + "ws": "~6.1.0", "xmlhttprequest-ssl": "~1.5.4", "yeast": "0.1.2" }, "dependencies": { + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -2247,6 +2304,24 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + }, + "ws": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", + "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "requires": { + "async-limiter": "~1.0.0" + } } } }, @@ -2276,19 +2351,19 @@ } }, "es-abstract": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", - "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.1", + "object.assign": "^4.1.0", "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" } @@ -2299,12 +2374,11 @@ "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" }, "es-get-iterator": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.1.tgz", - "integrity": "sha512-qorBw8Y7B15DVLaJWy6WdEV/ZkieBcu6QCq/xzWzGOKJqgG1j754vXRfZ3NY7HSShneqU43mPB4OkQBTkvHhFw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.1", + "es-abstract": "^1.17.4", "has-symbols": "^1.0.1", "is-arguments": "^1.0.4", "is-map": "^2.0.1", @@ -2520,11 +2594,6 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2695,9 +2764,9 @@ } }, "flat": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", "requires": { "is-buffer": "~2.0.3" } @@ -2738,9 +2807,9 @@ } }, "fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.0.tgz", + "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==", "dev": true }, "fs-extra": { @@ -3262,9 +3331,9 @@ } }, "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", "dev": true }, "get-browser-rtc": { @@ -3489,6 +3558,13 @@ "inherits": "^2.0.4", "readable-stream": "^3.6.0", "safe-buffer": "^5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "hash.js": { @@ -3501,9 +3577,9 @@ } }, "hasha": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", - "integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.0.tgz", + "integrity": "sha512-2W+jKdQbAdSIrggA8Q35Br8qKadTrqCTC8+XZvBWepKDK6m9XkX6Iz1a2yh2KP01kzAR/dpuMeUnocoLYDcskw==", "dev": true, "requires": { "is-stream": "^2.0.0", @@ -3526,9 +3602,9 @@ "integrity": "sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw=" }, "highlight.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.5.0.tgz", - "integrity": "sha512-xTmvd9HiIHR6L53TMC7TKolEj65zG1XU+Onr8oi86mYa+nLcIbxTTWkpW7CsEwv/vK7u1zb8alZIMLDqqN6KTw==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.4.1.tgz", + "integrity": "sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg==", "dev": true }, "hmac-drbg": { @@ -3639,6 +3715,13 @@ "lodash.padstart": "4.6.1", "lodash.repeat": "4.1.0", "sprintf-js": "1.1.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + } } }, "ip-regex": { @@ -3789,6 +3872,36 @@ } } } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -3813,6 +3926,17 @@ "protons": "^1.0.1", "streaming-iterables": "^5.0.2", "varint-decoder": "^0.4.0" + }, + "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } } }, "ipfs-block-service": { @@ -3916,6 +4040,15 @@ "multihashing-async": "~0.8.0" } }, + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + }, "multihashing-async": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/multihashing-async/-/multihashing-async-0.8.2.tgz", @@ -4141,6 +4274,15 @@ "protons": "^1.2.1", "stable": "^0.1.8" } + }, + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } } } }, @@ -4155,6 +4297,17 @@ "multicodec": "^1.0.0", "multihashes": "^1.0.1", "multihashing-async": "^1.0.0" + }, + "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } } }, "ipld-block": { @@ -4178,6 +4331,17 @@ "is-circular": "^1.0.2", "multicodec": "^1.0.3", "multihashing-async": "^1.0.0" + }, + "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } } }, "ipld-dag-pb": { @@ -4194,6 +4358,15 @@ "stable": "^0.1.8" }, "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + }, "multihashing-async": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/multihashing-async/-/multihashing-async-0.8.2.tgz", @@ -4224,6 +4397,17 @@ "multihashes": "^1.0.1", "multihashing-async": "^1.0.0", "rlp": "^2.2.4" + }, + "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } } }, "ipld-git": { @@ -4237,6 +4421,17 @@ "multihashing-async": "^1.0.0", "smart-buffer": "^4.1.0", "strftime": "^0.10.0" + }, + "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } } }, "ipld-raw": { @@ -4249,6 +4444,15 @@ "multihashing-async": "~0.8.1" }, "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + }, "multihashing-async": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/multihashing-async/-/multihashing-async-0.8.2.tgz", @@ -4275,7 +4479,18 @@ "multihashes": "^1.0.1", "multihashing-async": "^1.0.0", "zcash-block": "^2.0.0" - } + }, + "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } + } }, "ipns": { "version": "0.7.4", @@ -4321,12 +4536,9 @@ } }, "is-arguments": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", - "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", - "requires": { - "call-bind": "^1.0.0" - } + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" }, "is-bigint": { "version": "1.0.1", @@ -4350,14 +4562,14 @@ } }, "is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" }, "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" }, "is-ci": { "version": "2.0.0", @@ -4372,15 +4584,6 @@ "resolved": "https://registry.npmjs.org/is-circular/-/is-circular-1.0.2.tgz", "integrity": "sha512-YttjnrswnUYRVJvxCvu8z+PGMUSzC2JttP0OEXezlAEdp3EXzhf7IZ3j0gRAybJBQupedIZFhY61Tga6E0qASA==" }, - "is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", @@ -4477,9 +4680,9 @@ } }, "is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==" }, "is-negative-zero": { "version": "2.0.1", @@ -4517,17 +4720,17 @@ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" }, "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", "requires": { "has-symbols": "^1.0.1" } }, "is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==" }, "is-stream": { "version": "2.0.0", @@ -4578,6 +4781,30 @@ "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" } + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } } } }, @@ -4661,14 +4888,6 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.0.0", "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "istanbul-lib-processinfo": { @@ -4684,26 +4903,6 @@ "p-map": "^3.0.0", "rimraf": "^3.0.0", "uuid": "^3.3.3" - }, - "dependencies": { - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } } }, "istanbul-lib-report": { @@ -4724,9 +4923,9 @@ "dev": true }, "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -5033,9 +5232,10 @@ "dev": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -5311,11 +5511,6 @@ "util-deprecate": "~1.0.1" } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -5517,6 +5712,15 @@ "ursa-optional": "^0.10.1" }, "dependencies": { + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + }, "multihashing-async": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/multihashing-async/-/multihashing-async-0.8.2.tgz", @@ -5569,6 +5773,16 @@ "peer-id": "~0.13.3", "protons": "^1.0.1", "time-cache": "^0.3.0" + }, + "dependencies": { + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + } } }, "libp2p-gossipsub": { @@ -5586,6 +5800,16 @@ "peer-id": "~0.13.12", "protons": "^1.0.1", "time-cache": "^0.3.0" + }, + "dependencies": { + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + } } }, "libp2p-interfaces": { @@ -5665,6 +5889,14 @@ "murmurhash3js-revisited": "^3.0.0" } }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + }, "streaming-iterables": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/streaming-iterables/-/streaming-iterables-4.1.2.tgz", @@ -5985,9 +6217,14 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" }, "lodash.find": { "version": "4.6.0", @@ -6082,13 +6319,6 @@ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "requires": { "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } } }, "marked": { @@ -6127,11 +6357,6 @@ "requires": { "xtend": "~4.0.0" } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -6227,11 +6452,6 @@ "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -6241,13 +6461,6 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } } } } @@ -6274,11 +6487,18 @@ "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==" }, "mime-types": { - "version": "2.1.28", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", - "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "requires": { - "mime-db": "1.45.0" + "mime-db": "1.44.0" + }, + "dependencies": { + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + } } }, "mimic-response": { @@ -6351,14 +6571,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -6369,19 +6581,6 @@ "wrap-ansi": "^5.1.0" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", @@ -6405,6 +6604,15 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -6414,17 +6622,6 @@ "path-exists": "^3.0.0" } }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, "p-locate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -6456,11 +6653,6 @@ "ansi-regex": "^4.1.0" } }, - "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==" - }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -6595,12 +6787,19 @@ } }, "multicodec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", - "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-2.1.0.tgz", + "integrity": "sha512-7AYpK/avswOWvnqQ9/jOkQCS7Fp4aKxw5ojvn5gyK2VQTZz3YVXeLMzoIZDBy745JSfJMXkTS0ptnHci5Mt1mA==", "requires": { - "buffer": "^5.6.0", - "varint": "^5.0.0" + "uint8arrays": "1.1.0", + "varint": "^6.0.0" + }, + "dependencies": { + "varint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", + "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==" + } } }, "multihashes": { @@ -6800,32 +6999,17 @@ "spawn-wrap": "^2.0.0", "test-exclude": "^6.0.0", "yargs": "^15.0.2" - }, - "dependencies": { - "p-map": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", - "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } } }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" + }, "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" }, "object-is": { "version": "1.1.4", @@ -6842,14 +7026,14 @@ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" } }, "observable-webworkers": { @@ -6941,9 +7125,10 @@ } }, "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "dev": true, "requires": { "aggregate-error": "^3.0.0" } @@ -6994,6 +7179,16 @@ "integrity": "sha512-/Z7mcs8Liie8E7IHI9SBtmkHVW/GjLroQ94ALoAMIG20mqFMuh56/3WYhtOTqX9ccRSOxgaCkFC94Bat1Ofskg==", "requires": { "p-map": "^4.0.0" + }, + "dependencies": { + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + } } }, "p-try": { @@ -7007,9 +7202,9 @@ "integrity": "sha512-WyUjRAvK4CG9DUW21ZsNYcBj6guN7pgZAOFR8mUtyNXyPC5WUo3L48nxI5TsGEZ+VJhZXzyeH/Sxi2lxYcPp3A==" }, "p-wait-for": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.2.0.tgz", - "integrity": "sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.1.0.tgz", + "integrity": "sha512-0Uy19uhxbssHelu9ynDMcON6BmMk6pH8551CvxROhiz3Vx+yC4RqxjyIDk2V4ll0g9177RKT++PK4zcV58uJ7A==", "requires": { "p-timeout": "^3.0.0" } @@ -7035,13 +7230,6 @@ "registry-auth-token": "^4.0.0", "registry-url": "^5.0.0", "semver": "^6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } } }, "parse-asn1": { @@ -7067,14 +7255,20 @@ "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==" }, "parseqs": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", - "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "requires": { + "better-assert": "~1.0.0" + } }, "parseuri": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", - "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "requires": { + "better-assert": "~1.0.0" + } }, "path-exists": { "version": "4.0.0", @@ -7131,9 +7325,9 @@ } }, "peek-readable": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-3.1.1.tgz", - "integrity": "sha512-QHJag0oYYPVkx6rVPEgCLEUMo6VRYbV3GUrqy00lxXJBEIw9LhPCP5MQI6mEfahJO9KYUP8W8qD8kC0V9RyZFQ==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-3.1.0.tgz", + "integrity": "sha512-KGuODSTV6hcgdZvDrIDBUkN0utcAVj1LL7FfGbM0viKTtCHmtZcuEJ+lGqsp0fTFkGqesdtemV2yUSMeyy3ddA==" }, "peer-id": { "version": "0.13.13", @@ -7224,9 +7418,9 @@ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" }, "pretty-bytes": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.5.0.tgz", - "integrity": "sha512-p+T744ZyjjiaFlMUZZv6YPC5JrkNj8maRmPaQCWFJFplUAzpIUTRaTcS+7wmZtUoFXHtESJb23ISliaWyz3SHA==" + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.4.1.tgz", + "integrity": "sha512-s1Iam6Gwz3JI5Hweaz4GoCD1WUNUIyzePFy5+Js2hjwGVt2Z79wNN+ZKOZ2vB6C+Xs6njyB84Z1IthQg8d9LxA==" }, "process-nextick-args": { "version": "2.0.1", @@ -7322,9 +7516,9 @@ }, "dependencies": { "@types/node": { - "version": "10.17.50", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.50.tgz", - "integrity": "sha512-vwX+/ija9xKc/z9VqMCdbf4WYcMTGsI0I/L/6shIF3qXURxZOhPQlPRHtjTpiNhAwn0paMJzlOQqw6mAGEQnTA==" + "version": "10.17.49", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.49.tgz", + "integrity": "sha512-PGaJNs5IZz5XgzwJvL/1zRfZB7iaJ5BydZ8/Picm+lUNYoNO9iVTQkVy5eUh0dZDrx3rBOIs3GCbCRmMuYyqwg==" } } }, @@ -7489,6 +7683,11 @@ "resolve": "^1.1.6" } }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + }, "regexp.prototype.flags": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", @@ -7539,12 +7738,11 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", "dev": true, "requires": { - "is-core-module": "^2.1.0", "path-parse": "^1.0.6" } }, @@ -7573,9 +7771,10 @@ "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" }, "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, "requires": { "glob": "^7.1.3" } @@ -7605,9 +7804,9 @@ } }, "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safer-buffer": { "version": "2.1.2", @@ -7648,27 +7847,9 @@ "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==" }, "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "semver-diff": { "version": "3.1.1", @@ -7676,13 +7857,6 @@ "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", "requires": { "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } } }, "serialize-javascript": { @@ -7756,13 +7930,57 @@ } }, "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz", + "integrity": "sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==", "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "es-abstract": "^1.18.0-next.0", + "object-inspect": "^1.8.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } } }, "signal-exit": { @@ -7779,12 +7997,13 @@ } }, "sinon": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.3.tgz", - "integrity": "sha512-m+DyAWvqVHZtjnjX/nuShasykFeiZ+nPuEfD4G3gpvKGkXRhkF/6NSt2qN2FjZhfrcHXFzUzI+NLnk+42fnLEw==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.2.tgz", + "integrity": "sha512-9Owi+RisvCZpB0bdOVFfL314I6I4YoRlz6Isi4+fr8q8YQsDPoCe5UnmNtKHRThX3negz2bXHWIuiPa42vM8EQ==", "requires": { "@sinonjs/commons": "^1.8.1", "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/formatio": "^5.0.1", "@sinonjs/samsam": "^5.3.0", "diff": "^4.0.2", "nise": "^4.0.4", @@ -7812,24 +8031,79 @@ "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==" }, "socket.io": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz", - "integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", + "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", "requires": { "debug": "~4.1.0", - "engine.io": "~3.5.0", + "engine.io": "~3.4.0", "has-binary2": "~1.0.2", "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.4.0", + "socket.io-client": "2.3.0", "socket.io-parser": "~3.4.0" }, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "socket.io-client": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", + "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~4.1.0", + "engine.io-client": "~3.4.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" + }, + "dependencies": { + "socket.io-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz", + "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==", + "requires": { + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + } } } } @@ -7840,15 +8114,15 @@ "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" }, "socket.io-client": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz", - "integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.1.tgz", + "integrity": "sha512-YXmXn3pA8abPOY//JtYxou95Ihvzmg8U6kQyolArkIyLd0pgVhrfor/iMsox8cn07WCOOvvuJ6XKegzIucPutQ==", "requires": { "backo2": "1.0.2", "component-bind": "1.0.0", "component-emitter": "~1.3.0", "debug": "~3.1.0", - "engine.io-client": "~3.5.0", + "engine.io-client": "~3.4.0", "has-binary2": "~1.0.2", "indexof": "0.0.1", "parseqs": "0.0.6", @@ -7857,6 +8131,11 @@ "to-array": "0.1.4" }, "dependencies": { + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -7875,6 +8154,16 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + }, "socket.io-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz", @@ -7897,19 +8186,6 @@ "isarray": "2.0.1" }, "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, "isarray": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", @@ -7927,9 +8203,9 @@ } }, "sort-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz", - "integrity": "sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-4.1.0.tgz", + "integrity": "sha512-/sRdxzkkPFUYiCrTr/2t+104nDc9AgDmEpeVYuvOWYQe3Djk1GWO6lVw3Vx2jfh1SsR0eehhd1nvFYlzt5e99w==", "requires": { "is-plain-obj": "^2.0.0" } @@ -7957,17 +8233,6 @@ "rimraf": "^3.0.0", "signal-exit": "^3.0.2", "which": "^2.0.1" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } } }, "split2": { @@ -7979,9 +8244,9 @@ } }, "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "stable": { "version": "0.1.8", @@ -8017,21 +8282,21 @@ } }, "string.prototype.trimend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", - "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" } }, "string.prototype.trimstart": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", - "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" } }, "string_decoder": { @@ -8040,6 +8305,13 @@ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "requires": { "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } } }, "strip-ansi": { @@ -8065,18 +8337,18 @@ } }, "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==" }, "strtok3": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.0.6.tgz", - "integrity": "sha512-fVxvAEKDwHFfbQO1yKxKBPfkWZyBr0Zf20UQ/mblbkAQe5h0Xdd2jDb3Mh7yRZd7LSItJ9JWgQWelpEmVoBe2g==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.0.4.tgz", + "integrity": "sha512-rqWMKwsbN9APU47bQTMEYTPcwdpKDtmf1jVhHzNW2cL1WqAxaM9iBb9t5P2fj+RV2YsErUWgQzHD5JwV0uCTEQ==", "requires": { "@tokenizer/token": "^0.1.1", "@types/debug": "^4.1.5", - "peek-readable": "^3.1.1" + "peek-readable": "^3.1.0" } }, "supports-color": { @@ -8111,6 +8383,14 @@ "requires": { "minimist": "^1.2.5" } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "requires": { + "glob": "^7.1.3" + } } } }, @@ -8311,9 +8591,9 @@ "dev": true }, "uglify-js": { - "version": "3.12.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.4.tgz", - "integrity": "sha512-L5i5jg/SHkEqzN18gQMTWsZk3KelRsfD1wUVNqtq0kzqWQqcJjyL8yc1o8hJgRrWqrAl2mUFbhfznEIoi7zi2A==", + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.2.tgz", + "integrity": "sha512-rWYleAvfJPjduYCt+ELvzybNah/zIkRteGXIBO8X0lteRZPGladF61hFi8tU7qKTsF7u6DUQCtT9k00VlFOgkg==", "dev": true, "optional": true }, @@ -8375,6 +8655,14 @@ "xdg-basedir": "^4.0.0" }, "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, "chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -8384,6 +8672,19 @@ "supports-color": "^7.1.0" } }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -8545,6 +8846,30 @@ "string.prototype.trimend": "^1.0.1", "string.prototype.trimstart": "^1.0.1" } + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } } } }, @@ -8620,6 +8945,30 @@ "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } } }, "wrappy": { @@ -8658,6 +9007,14 @@ "resolved": "https://registry.npmjs.org/xor-distance/-/xor-distance-2.0.0.tgz", "integrity": "sha512-AsAqZfPAuWx7qB/0kyRDUEvoU3QKsHWzHU9smFlkaiprEpGfJ/NBbLze2Uq0rdkxCxkNM9uOLvz/KoNBCbZiLQ==" }, + "xregexp": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz", + "integrity": "sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==", + "requires": { + "@babel/runtime-corejs3": "^7.8.3" + } + }, "xsalsa20": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/xsalsa20/-/xsalsa20-1.1.0.tgz", @@ -8669,9 +9026,9 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" }, "yallist": { "version": "2.1.2", @@ -8679,12 +9036,12 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.0.tgz", + "integrity": "sha512-D3fRFnZwLWp8jVAAhPZBsmeIHY8tTsb8ItV9KaAaopmC6wde2u6Yw29JBIZHXw14kgkRnYmDgmQU4FVMDlIsWw==", "requires": { "cliui": "^6.0.0", - "decamelize": "^1.2.0", + "decamelize": "^3.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", @@ -8694,6 +9051,16 @@ "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" + }, + "dependencies": { + "decamelize": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-3.2.0.tgz", + "integrity": "sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw==", + "requires": { + "xregexp": "^4.2.4" + } + } } }, "yargs-parser": { @@ -8725,14 +9092,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -8743,19 +9102,6 @@ "wrap-ansi": "^5.1.0" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", diff --git a/src/chains/filecoin/filecoin/package.json b/src/chains/filecoin/filecoin/package.json index 7463f9393d..e4977f5fe4 100644 --- a/src/chains/filecoin/filecoin/package.json +++ b/src/chains/filecoin/filecoin/package.json @@ -5,6 +5,10 @@ "author": "Tim Coulter", "homepage": "https://github.com/trufflesuite/ganache-core/tree/develop/src/filecoin#readme", "license": "MIT", + "engines": { + "node": ">=12.13.0 <=14.14.0", + "npm": ">=6.4.1" + }, "main": "lib/index.js", "types": "lib/index.d.ts", "source": "index.ts", @@ -27,9 +31,10 @@ "docs.build": "rm -rf ./lib/docs ./lib/api.json && npm run docs.typedoc", "docs.typedoc": "typedoc --options ./typedoc.json --readme ./README.md --out ../../../docs/typedoc --json ../../../docs/typedoc/api.json src/api.ts", "docs.preview": "ws --open --port 3012 --directory ../../../", - "tsc": "ttsc", + "tsc": "ttsc --build", + "tsc.declarations": "ttsc --build tsconfig.declarations.json", "test": "nyc --reporter lcov npm run mocha", - "mocha": "cross-env TS_NODE_COMPILER=ttypescript TS_NODE_FILES=true mocha --exit --check-leaks --throw-deprecation --trace-warnings --require ts-node/register --timeout 10000 'tests/**/*.test.ts'" + "mocha": "cross-env TS_NODE_COMPILER=ttypescript TS_NODE_FILES=true ts-node ../../../../scripts/lerna-mocha.ts --timeout 10000 --require ts-node/register '_PACKAGEDIR_/tests/**/*.test.ts'" }, "bugs": { "url": "https://github.com/trufflesuite/ganache-core/issues" @@ -45,7 +50,7 @@ "tooling" ], "dependencies": { - "@filecoin-shipyard/lotus-client-schema": "0.0.12", + "@filecoin-shipyard/lotus-client-schema": "2.0.0", "@ganache/filecoin-options": "0.1.0", "@ganache/options": "0.1.0", "@ganache/utils": "0.1.0", @@ -59,8 +64,9 @@ "ipfs": "0.48.1", "ipfs-http-client": "45.0.0", "ipld-dag-cbor": "0.16.0", + "lodash.clonedeep": "4.5.0", "mocha": "8.0.1", - "multicodec": "1.0.4", + "multicodec": "2.1.0", "multihashing": "0.3.3", "noble-bls12-381": "0.4.3", "seedrandom": "3.0.5" @@ -68,9 +74,14 @@ "devDependencies": { "@filecoin-shipyard/lotus-client-provider-browser": "0.0.14", "@filecoin-shipyard/lotus-client-rpc": "0.0.11", + "@types/deep-equal": "1.0.1", + "@types/lodash.clonedeep": "4.5.6", + "@types/seedrandom": "2.4.28", + "@types/ws": "7.4.0", "cross-env": "7.0.2", "nyc": "15.1.0", "typedoc": "0.17.8", - "uWebSockets.js": "github:uNetworking/uWebSockets.js#v18.4.0" + "uWebSockets.js": "github:uNetworking/uWebSockets.js#v18.4.0", + "ws": "7.4.2" } } diff --git a/src/chains/filecoin/filecoin/src/@types/@filecoin-shipyard/lotus-client-schema/index.d.ts b/src/chains/filecoin/filecoin/src/@types/@filecoin-shipyard/lotus-client-schema/index.d.ts new file mode 100644 index 0000000000..097a9a2d06 --- /dev/null +++ b/src/chains/filecoin/filecoin/src/@types/@filecoin-shipyard/lotus-client-schema/index.d.ts @@ -0,0 +1,20 @@ +declare module "@filecoin-shipyard/lotus-client-schema" { + export type Schema = { + methods: { + [propertyName: string]: { + subscription?: boolean; + }; + }; + }; + + export type MainNetDeclaration = { + common: Schema; + fullNode: Schema; + storageMiner: Schema; + gatewayApi: Schema; + walletApi: Schema; + workerApi: Schema; + }; + + let mainnet: MainNetDeclaration; +} diff --git a/src/chains/filecoin/filecoin/src/@types/@types/@filecoin-shipyard/lotus-client-schema/index.d.ts b/src/chains/filecoin/filecoin/src/@types/@types/@filecoin-shipyard/lotus-client-schema/index.d.ts deleted file mode 100644 index f9d34294c4..0000000000 --- a/src/chains/filecoin/filecoin/src/@types/@types/@filecoin-shipyard/lotus-client-schema/index.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -declare module "@filecoin-shipyard/lotus-client-schema" { - export type Schema = { - methods: { - [propertyName: string]: {}; - }; - }; - - export type TestNetDeclaration = { - fullNode: Schema; - storageMiner: Schema; - }; - - let testnet: TestNetDeclaration; -} diff --git a/src/chains/filecoin/filecoin/src/@types/@types/base32-encoding/index.d.ts b/src/chains/filecoin/filecoin/src/@types/base32-encoding/index.d.ts similarity index 100% rename from src/chains/filecoin/filecoin/src/@types/@types/base32-encoding/index.d.ts rename to src/chains/filecoin/filecoin/src/@types/base32-encoding/index.d.ts diff --git a/src/chains/filecoin/filecoin/src/@types/@types/blakejs/index.d.ts b/src/chains/filecoin/filecoin/src/@types/blakejs/index.d.ts similarity index 60% rename from src/chains/filecoin/filecoin/src/@types/@types/blakejs/index.d.ts rename to src/chains/filecoin/filecoin/src/@types/blakejs/index.d.ts index 89d6f4c5cf..ccbde08864 100644 --- a/src/chains/filecoin/filecoin/src/@types/@types/blakejs/index.d.ts +++ b/src/chains/filecoin/filecoin/src/@types/blakejs/index.d.ts @@ -1,7 +1,7 @@ declare module "blakejs" { type Blake = { - blake2b(buf: Buffer, key: Uint8Array, outLen: number): Uint8Array; - blake2s(buf: Buffer, key: Uint8Array, outLen: number): Uint8Array; + blake2b(buf: Buffer, key: Uint8Array | null, outLen: number): Uint8Array; + blake2s(buf: Buffer, key: Uint8Array | null, outLen: number): Uint8Array; blake2sInit(outputLen: number, obj: null): any; blake2sUpdate(context: any, buf: Buffer); diff --git a/src/chains/filecoin/filecoin/src/@types/@types/borc/index.d.ts b/src/chains/filecoin/filecoin/src/@types/borc/index.d.ts similarity index 100% rename from src/chains/filecoin/filecoin/src/@types/@types/borc/index.d.ts rename to src/chains/filecoin/filecoin/src/@types/borc/index.d.ts diff --git a/src/chains/filecoin/filecoin/src/@types/@types/ipfs-http-client/index.d.ts b/src/chains/filecoin/filecoin/src/@types/ipfs-http-client/index.d.ts similarity index 100% rename from src/chains/filecoin/filecoin/src/@types/@types/ipfs-http-client/index.d.ts rename to src/chains/filecoin/filecoin/src/@types/ipfs-http-client/index.d.ts diff --git a/src/chains/filecoin/filecoin/src/@types/@types/ipld-dag-cbor/index.d.ts b/src/chains/filecoin/filecoin/src/@types/ipld-dag-cbor/index.d.ts similarity index 100% rename from src/chains/filecoin/filecoin/src/@types/@types/ipld-dag-cbor/index.d.ts rename to src/chains/filecoin/filecoin/src/@types/ipld-dag-cbor/index.d.ts diff --git a/src/chains/filecoin/filecoin/src/@types/multihashing/index.d.ts b/src/chains/filecoin/filecoin/src/@types/multihashing/index.d.ts new file mode 100644 index 0000000000..97acee4f2f --- /dev/null +++ b/src/chains/filecoin/filecoin/src/@types/multihashing/index.d.ts @@ -0,0 +1 @@ +declare module "multihashing"; diff --git a/src/chains/filecoin/filecoin/src/api.ts b/src/chains/filecoin/filecoin/src/api.ts index 741964c5c9..3b92273f84 100644 --- a/src/chains/filecoin/filecoin/src/api.ts +++ b/src/chains/filecoin/filecoin/src/api.ts @@ -1,79 +1,166 @@ //#region Imports -import { types } from "@ganache/utils"; +import { types, Quantity, PromiEvent, Subscription } from "@ganache/utils"; import Blockchain from "./blockchain"; import { - StorageProposal, - SerializedStorageProposal -} from "./things/storage-proposal"; + StartDealParams, + SerializedStartDealParams +} from "./things/start-deal-params"; import { SerializedRootCID, RootCID } from "./things/root-cid"; -import { SerializedDeal } from "./things/deal"; -import { SerializedTipset } from "./things/tipset"; +import { SerializedDealInfo } from "./things/deal-info"; +import { SerializedTipset, Tipset } from "./things/tipset"; import { SerializedAddress } from "./things/address"; -import { SerializedMiner } from "./things/miner"; import { - SerializedRetrievalOffer, - RetrievalOffer -} from "./things/retrieval-offer"; - -const _blockchain = Symbol("blockchain"); + SerializedRetrievalOrder, + RetrievalOrder +} from "./things/retrieval-order"; +import { SerializedQueryOffer } from "./things/query-offer"; +import Emittery from "emittery"; +import { HeadChange, HeadChangeType } from "./things/head-change"; +import { SubscriptionMethod, SubscriptionId } from "./types/subscriptions"; export default class FilecoinApi implements types.Api { readonly [index: string]: (...args: any) => Promise; - private readonly [_blockchain]: Blockchain; + readonly #getId = (id => () => Quantity.from(++id))(0); + readonly #subscriptions = new Map(); + readonly #blockchain: Blockchain; constructor(blockchain: Blockchain) { - this[_blockchain] = blockchain; + this.#blockchain = blockchain; } async stop(): Promise { - return await this[_blockchain].stop(); + return await this.#blockchain.stop(); } async "Filecoin.ChainGetGenesis"(): Promise { - return this[_blockchain].latestTipset().serialize(); + return this.#blockchain.latestTipset().serialize(); } async "Filecoin.ChainHead"(): Promise { - return this[_blockchain].latestTipset().serialize(); + return this.#blockchain.latestTipset().serialize(); + } + + // Reference implementation entry point: https://git.io/JtO3a + "Filecoin.ChainNotify"(rpcId?: string): PromiEvent { + const subscriptionId = this.#getId(); + let promiEvent: PromiEvent; + + const currentHead = new HeadChange({ + type: HeadChangeType.HCCurrent, + val: this.#blockchain.latestTipset() + }); + + const unsubscribeFromEmittery = this.#blockchain.on( + "tipset", + (tipset: Tipset) => { + // Ganache currently doesn't support Filecoin reorgs, + // so we'll always only have one tipset per head change + // See reference implementations here: https://git.io/JtOOk; + // other lines of interest are line 207 which shows only the chainstore only + // references the "hcnf" (head change notification function) in the + // reorgWorker function (lines 485-560) + + // Ganache currently doesn't support Filecoin reverts, + // so we'll always use HCApply for now + + const newHead = new HeadChange({ + type: HeadChangeType.HCApply, + val: tipset + }); + + if (promiEvent) { + promiEvent.emit("message", { + type: SubscriptionMethod.ChannelUpdated, + data: [subscriptionId.toString(), [newHead.serialize()]] + }); + } + } + ); + + const unsubscribe = (): void => { + unsubscribeFromEmittery(); + // Per https://git.io/JtOc1 and https://git.io/JtO3H + // implementations, we're should cancel the subscription + // since the protocol technically supports multiple channels + // per subscription, but implementation seems to show that there's + // only one channel per subscription + if (rpcId) { + promiEvent.emit("message", { + type: SubscriptionMethod.SubscriptionCanceled, + data: [rpcId] + }); + } + }; + + promiEvent = PromiEvent.resolve({ + unsubscribe, + id: subscriptionId + }); + + // There currently isn't an unsubscribe method, + // but it would go here + this.#subscriptions.set(subscriptionId.toString()!, unsubscribe); + + promiEvent.emit("message", { + type: SubscriptionMethod.ChannelUpdated, + data: [subscriptionId.toString(), [currentHead.serialize()]] + }); + + return promiEvent; + } + + [SubscriptionMethod.ChannelClosed]( + subscriptionId: SubscriptionId + ): Promise { + const subscriptions = this.#subscriptions; + const unsubscribe = this.#subscriptions.get(subscriptionId); + + if (unsubscribe) { + subscriptions.delete(subscriptionId); + unsubscribe(); + return Promise.resolve(true); + } else { + return Promise.resolve(false); + } } - async "Filecoin.StateListMiners"(): Promise> { - return [this[_blockchain].miner.serialize()]; + async "Filecoin.StateListMiners"(): Promise> { + return [this.#blockchain.miner]; } async "Filecoin.WalletDefaultAddress"(): Promise { - return this[_blockchain].address.serialize(); + return this.#blockchain.address.serialize(); } async "Filecoin.WalletBalance"(address: string): Promise { - let managedAddress = this[_blockchain].address; + let managedAddress = this.#blockchain.address; // For now, anything but our default address will have no balance if (managedAddress.value == address) { - return this[_blockchain].balance.serialize(); + return this.#blockchain.balance.serialize(); } else { return "0"; } } async "Filecoin.ClientStartDeal"( - serializedProposal: SerializedStorageProposal + serializedProposal: SerializedStartDealParams ): Promise { - let proposal = new StorageProposal(serializedProposal); - let proposalRootCid = await this[_blockchain].startDeal(proposal); + let proposal = new StartDealParams(serializedProposal); + let proposalRootCid = await this.#blockchain.startDeal(proposal); return proposalRootCid.serialize(); } - async "Filecoin.ClientListDeals"(): Promise> { - return this[_blockchain].deals.map(deal => deal.serialize()); + async "Filecoin.ClientListDeals"(): Promise> { + return this.#blockchain.deals.map(deal => deal.serialize()); } async "Filecoin.ClientFindData"( rootCid: SerializedRootCID - ): Promise> { - let remoteOffer = await this[_blockchain].createRetrievalOffer( + ): Promise> { + let remoteOffer = await this.#blockchain.createQueryOffer( new RootCID(rootCid) ); return [remoteOffer.serialize()]; @@ -82,13 +169,13 @@ export default class FilecoinApi implements types.Api { async "Filecoin.ClientHasLocal"( rootCid: SerializedRootCID ): Promise { - return await this[_blockchain].hasLocal(rootCid["/"]); + return await this.#blockchain.hasLocal(rootCid["/"]); } async "Filecoin.ClientRetrieve"( - retrievalOffer: SerializedRetrievalOffer + retrievalOffer: SerializedRetrievalOrder ): Promise { - await this[_blockchain].retrieve(new RetrievalOffer(retrievalOffer)); + await this.#blockchain.retrieve(new RetrievalOrder(retrievalOffer)); // Return value is a placeholder. // @@ -99,8 +186,8 @@ export default class FilecoinApi implements types.Api { return {}; } - async "Filecoin.GanacheMineTipset"(): Promise { - await this[_blockchain].mineTipset(); - return this[_blockchain].latestTipset().serialize(); + async "Ganache.MineTipset"(): Promise { + await this.#blockchain.mineTipset(); + return this.#blockchain.latestTipset().serialize(); } } diff --git a/src/chains/filecoin/filecoin/src/blockchain.ts b/src/chains/filecoin/filecoin/src/blockchain.ts index 2883b68ec4..597399e4e1 100644 --- a/src/chains/filecoin/filecoin/src/blockchain.ts +++ b/src/chains/filecoin/filecoin/src/blockchain.ts @@ -1,24 +1,24 @@ import { Tipset } from "./things/tipset"; -import { Block } from "./things/block"; +import { BlockHeader } from "./things/block-header"; import { CID } from "./things/cid"; import { RootCID } from "./things/root-cid"; import { utils } from "@ganache/utils"; import Emittery from "emittery"; -import { Miner } from "./things/miner"; import { Address } from "./things/address"; -import { Deal } from "./things/deal"; +import { DealInfo } from "./things/deal-info"; import Balance from "./things/balance"; -import { StorageProposal } from "./things/storage-proposal"; -import { DealState } from "./deal-state"; +import { StartDealParams } from "./things/start-deal-params"; +import { StorageDealStatus } from "./types/storage-deal-status"; import IPFSServer, { IPFSNode } from "./ipfs-server"; import dagCBOR from "ipld-dag-cbor"; -import { RetrievalOffer } from "./things/retrieval-offer"; -import seedrandom from "seedrandom"; -import BN from "bn.js"; +import { RetrievalOrder } from "./things/retrieval-order"; import { FilecoinInternalOptions } from "@ganache/filecoin-options"; +import { QueryOffer } from "./things/query-offer"; +import { Ticket } from "./things/ticket"; export type BlockchainEvents = { ready(): void; + tipset: Tipset; }; export default class Blockchain extends Emittery.Typed< @@ -26,24 +26,23 @@ export default class Blockchain extends Emittery.Typed< keyof BlockchainEvents > { readonly tipsets: Array = []; - readonly miner: Miner; + readonly miner: string; // using string until we can support more address types in Address readonly address: Address; - readonly privateKey: string; #balance: Balance; get balance(): Balance { return this.#balance; } - readonly deals: Array = []; - readonly dealsByCid: Record = {}; - readonly inProcessDeals: Array = []; + readonly deals: Array = []; + readonly dealsByCid: Record = {}; + readonly inProcessDeals: Array = []; readonly options: FilecoinInternalOptions; private ipfsServer: IPFSServer; - private miningTimeout: NodeJS.Timeout; - private rng: () => number; + private miningTimeout: NodeJS.Timeout | null; + private rng: utils.RandomNumberGenerator; private ready: boolean; @@ -51,49 +50,56 @@ export default class Blockchain extends Emittery.Typed< super(); this.options = options; - if (this.options.miner.blockTime > 0) { - this.options.miner.automining = false; - } + this.rng = new utils.RandomNumberGenerator(this.options.wallet.seed); - if (this.options.wallet.seed) { - this.rng = seedrandom.alea(this.options.wallet.seed); - } else { - this.rng = Math.random; - } - - this.miner = new Miner(); + this.miner = "t01000"; this.address = Address.random(this.rng); this.#balance = new Balance(); this.ready = false; // Create genesis tipset + const genesisBlock = new BlockHeader({ + ticket: new Ticket({ + // Reference implementation https://git.io/Jt31s + vrfProof: this.rng.getBuffer(32) + }), + parents: [ + // Both lotus and lotus-devnet always have the Filecoin genesis CID + // hardcoded here. Reference implementation: https://git.io/Jt3oK + new RootCID({ + "/": "bafyreiaqpwbbyjo4a42saasj36kkrpv4tsherf2e7bvezkert2a7dhonoi" + }) + ] + }); this.tipsets.push( new Tipset({ - blocks: [new Block()], + blocks: [genesisBlock], height: 0 }) ); - setTimeout(async () => { - // Create the IPFS server - this.ipfsServer = new IPFSServer(this.options.chain.ipfsPort); + // Create the IPFS server + this.ipfsServer = new IPFSServer(this.options.chain.ipfsPort); - await this.ipfsServer.start(); + // Fire up the miner if necessary + if (this.options.miner.blockTime > 0) { + const intervalMine = () => { + this.mineTipset(); + }; - // Fire up the miner if necessary - if (!this.options.miner.automining && this.options.miner.blockTime != 0) { - const intervalMine = () => { - this.mineTipset(); - }; + this.miningTimeout = setInterval( + intervalMine, + this.options.miner.blockTime * 1000 + ); - this.miningTimeout = setInterval( - intervalMine, - this.options.miner.blockTime - ); + utils.unref(this.miningTimeout); + } else { + this.miningTimeout = null; + } - utils.unref(this.miningTimeout); - } + setTimeout(async () => { + await this.ipfsServer.start(); // Get this party started! this.ready = true; @@ -118,11 +124,13 @@ export default class Blockchain extends Emittery.Typed< * Gracefully shuts down the blockchain service and all of its dependencies. */ async stop() { - clearInterval(this.miningTimeout); + if (this.miningTimeout) { + clearInterval(this.miningTimeout); + } await this.ipfsServer.stop(); } - get ipfs(): IPFSNode { + get ipfs(): IPFSNode | null { return this.ipfsServer.node; } @@ -138,39 +146,53 @@ export default class Blockchain extends Emittery.Typed< // previous tipset is the parent of the new blocks. async mineTipset(numNewBlocks: number = 1): Promise { let previousTipset: Tipset = this.latestTipset(); + const newTipsetHeight = previousTipset.height + 1; - let newBlocks: Array = []; + let newBlocks: Array = []; for (let i = 0; i < numNewBlocks; i++) { newBlocks.push( - new Block({ + new BlockHeader({ miner: this.miner, - parents: [previousTipset.cids[0]] + parents: [previousTipset.cids[0]], + height: newTipsetHeight, + // Determined by interpreting the description of `weight` + // as an accumulating weight of win counts (which default to 1) + // See the description here: https://spec.filecoin.io/#section-glossary.weight + parentWeight: + BigInt(previousTipset.blocks[0].electionProof.winCount) + + previousTipset.blocks[0].parentWeight }) ); } let newTipset = new Tipset({ blocks: newBlocks, - height: previousTipset.height + 1 + height: newTipsetHeight }); this.tipsets.push(newTipset); // Advance the state of all deals in process. for (const deal of this.inProcessDeals) { - deal.advanceState(this.options.miner.automining); + deal.advanceState(); - if (deal.state == DealState.Active) { + if (deal.state == StorageDealStatus.Active) { // Remove the deal from the in-process array this.inProcessDeals.splice(this.inProcessDeals.indexOf(deal), 1); } } this.logLatestTipset(); + + this.emit("tipset", newTipset); } async hasLocal(cid: string): Promise { + if (!this.ipfsServer.node) { + return false; + } + try { // This stat will fail if the object doesn't exist. await this.ipfsServer.node.object.stat(cid, { @@ -183,6 +205,10 @@ export default class Blockchain extends Emittery.Typed< } private async getIPFSObjectSize(cid: string): Promise { + if (!this.ipfsServer.node) { + return 0; + } + let stat = await this.ipfsServer.node.object.stat(cid, { timeout: 500 // Enforce a timeout; otherwise will hang if CID not found }); @@ -190,9 +216,9 @@ export default class Blockchain extends Emittery.Typed< return stat.DataSize; } - async startDeal(proposal: StorageProposal): Promise { + async startDeal(proposal: StartDealParams): Promise { // Get size of IPFS object represented by the proposal - let size = await this.getIPFSObjectSize(proposal.data.root["/"].value); + let size = await this.getIPFSObjectSize(proposal.data.root.root.value); let signature = await this.address.signProposal(proposal); @@ -202,11 +228,11 @@ export default class Blockchain extends Emittery.Typed< let proposalRawCid = await dagCBOR.util.cid(signature.toString("hex")); let proposalCid = new CID(proposalRawCid.toString()); - let deal = new Deal({ + let deal = new DealInfo({ proposalCid: new RootCID({ - "/": proposalCid + root: proposalCid }), - state: DealState.Validating, // Not sure if this is right, but we'll start here + state: StorageDealStatus.Validating, // Not sure if this is right, but we'll start here message: "", provider: this.miner, pieceCid: null, @@ -225,46 +251,44 @@ export default class Blockchain extends Emittery.Typed< // If we're automining, mine a new block. Note that this will // automatically advance the deal to the active state. - if (this.options.miner.automining) { - while (deal.state != DealState.Active) { + if (this.options.miner.blockTime === 0) { + while (deal.state !== StorageDealStatus.Active) { this.mineTipset(); } } // Subtract the cost from our current balance - let totalPrice = new BN(deal.pricePerEpoch) - .mul(new BN(deal.duration)) - .toString(10); + let totalPrice = BigInt(deal.pricePerEpoch) * BigInt(deal.duration); this.#balance = this.#balance.sub(totalPrice); return deal.proposalCid; } - async createRetrievalOffer(rootCid: RootCID): Promise { - let size = await this.getIPFSObjectSize(rootCid["/"].value); + async createQueryOffer(rootCid: RootCID): Promise { + let size = await this.getIPFSObjectSize(rootCid.root.value); - return new RetrievalOffer({ + return new QueryOffer({ root: rootCid, size: size, miner: this.miner, - minPrice: "" + size * 2 // This seems to be what powergate does + minPrice: BigInt(size * 2) // This seems to be what powergate does }); } - async retrieve(retrievalOffer: RetrievalOffer): Promise { + async retrieve(retrievalOrder: RetrievalOrder): Promise { // Since this is a simulator, we're not actually interacting with other // IPFS and Filecoin nodes. Because of this, there's no need to // actually retrieve anything. That said, we'll check to make sure // we have the content locally in our IPFS server, and error if it // doesn't exist. - let hasLocal: boolean = await this.hasLocal(retrievalOffer.root["/"].value); + let hasLocal: boolean = await this.hasLocal(retrievalOrder.root.root.value); if (!hasLocal) { - throw new Error(`Object not found: ${retrievalOffer.root["/"].value}`); + throw new Error(`Object not found: ${retrievalOrder.root.root.value}`); } - this.#balance = this.#balance.sub(retrievalOffer.minPrice); + this.#balance = this.#balance.sub(retrievalOrder.total); } private logLatestTipset() { @@ -272,7 +296,7 @@ export default class Blockchain extends Emittery.Typed< let tipset = this.latestTipset(); this.options.logging.logger.log( - `${date} INFO New heaviest tipset! [${tipset.cids[0]["/"].value}] (height=${tipset.height})` + `${date} INFO New heaviest tipset! [${tipset.cids[0].root.value}] (height=${tipset.height})` ); } } diff --git a/src/chains/filecoin/filecoin/src/connector.ts b/src/chains/filecoin/filecoin/src/connector.ts index 59ff08ae13..f5b691509f 100644 --- a/src/chains/filecoin/filecoin/src/connector.ts +++ b/src/chains/filecoin/filecoin/src/connector.ts @@ -1,17 +1,15 @@ import Emittery from "emittery"; import FilecoinApi from "./api"; -import { JsonRpcTypes, types, utils, PromiEvent } from "@ganache/utils"; +import { JsonRpcTypes, types, utils } from "@ganache/utils"; import FilecoinProvider from "./provider"; -import { RecognizedString, HttpRequest } from "uWebSockets.js"; +import { RecognizedString, HttpRequest, WebSocket } from "uWebSockets.js"; import { FilecoinProviderOptions } from "@ganache/filecoin-options"; -export type ProviderOptions = FilecoinProviderOptions; export type Provider = FilecoinProvider; export const Provider = FilecoinProvider; -export const FlavorName = "filecoin" as const; export class Connector - extends Emittery.Typed + extends Emittery.Typed<{}, "ready" | "close"> implements types.Connector< FilecoinApi, @@ -25,7 +23,7 @@ export class Connector } constructor( - providerOptions: ProviderOptions = null, + providerOptions: FilecoinProviderOptions = {}, executor: utils.Executor ) { super(); @@ -45,15 +43,11 @@ export class Connector return JSON.parse(message) as JsonRpcTypes.Request; } - // Note that if we allow Filecoin to support Websockets, ws-server.ts blows up. - // TODO: Look into this. handle( payload: JsonRpcTypes.Request, - _connection: HttpRequest /*| WebSocket*/ - ): PromiEvent { - return new PromiEvent(resolve => { - return this.#provider.send(payload).then(resolve); - }); + _connection: HttpRequest | WebSocket + ): Promise { + return this.#provider._requestRaw(payload); } format( @@ -69,7 +63,7 @@ export class Connector payload: JsonRpcTypes.Request ): RecognizedString { const json = JsonRpcTypes.Error( - payload && payload.id ? payload.id : null, + payload && payload.id ? payload.id : undefined, error ); return JSON.stringify(json); diff --git a/src/chains/filecoin/filecoin/src/deal-state.ts b/src/chains/filecoin/filecoin/src/deal-state.ts deleted file mode 100644 index c07c37b3de..0000000000 --- a/src/chains/filecoin/filecoin/src/deal-state.ts +++ /dev/null @@ -1,64 +0,0 @@ -// Note that the runtime number values of these enums match -// *exactly* the number values of the same states on the Lotus server. -// Don't reorganize unless you know what you're doing. - -export enum DealState { - Unknown, - ProposalNotFound, - ProposalRejected, - ProposalAccepted, - Staged, - Sealing, - Active, - Failing, - NotFound, - FundsEnsured, // Deposited funds as neccesary to create a deal, ready to move forward - Validating, // Verifying that deal parameters are good - Transferring, // Moving data - WaitingForData, // Manual transfer - VerifyData, // Verify transferred data - generate CAR / piece data - EnsureProviderFunds, // Ensuring that provider collateral is sufficient - EnsureClientFunds, // Ensuring that client funds are sufficient - ProviderFunding, // Waiting for funds to appear in Provider balance - ClientFunding, // Waiting for funds to appear in Client balance - Publish, // Publishing deal to chain - Publishing, // Waiting for deal to appear on chain - Error, // deal failed with an unexpected error - Completed // on provider side, indicates deal is active and info for retrieval is recorded -} - -export let terminalStates: Array = [ - // go-fil-markets/storagemarket/types.go - DealState.ProposalNotFound, - DealState.ProposalRejected, - DealState.Error, - DealState.Completed -]; - -export let nextSuccessfulState: Record = [ - DealState.Validating, - DealState.Staged, - DealState.EnsureProviderFunds, - DealState.EnsureClientFunds, - DealState.FundsEnsured, - DealState.ProviderFunding, - DealState.ClientFunding, - DealState.Publish, - DealState.Publishing, - DealState.Transferring, - DealState.Sealing, - DealState.Active -].reduce((obj, currentValue, index, array) => { - // This creates an object linking each state to its next state - - let nextValue: DealState; - if (index + 1 < array.length) { - nextValue = array[index + 1]; - } else { - nextValue = array[index]; - } - - obj[currentValue] = nextValue; - - return obj; -}, {} as Record); diff --git a/src/chains/filecoin/filecoin/src/ipfs-server.ts b/src/chains/filecoin/filecoin/src/ipfs-server.ts index dcd6e855e5..7023d8be17 100644 --- a/src/chains/filecoin/filecoin/src/ipfs-server.ts +++ b/src/chains/filecoin/filecoin/src/ipfs-server.ts @@ -42,15 +42,17 @@ type IPFSHttpServer = { class IPFSServer { static readonly DEFAULT_PORT = 5001; - public readonly serverPort = 43134; - public readonly apiPort = IPFSServer.DEFAULT_PORT; + public readonly serverPort: number = 43134; + public readonly apiPort: number = IPFSServer.DEFAULT_PORT; - public node: IPFSNode; + public node: IPFSNode | null; - private httpServer: IPFSHttpServer; + private httpServer: IPFSHttpServer | null; - constructor(apiPort) { + constructor(apiPort: number) { this.apiPort = apiPort; + this.node = null; + this.httpServer = null; } async start() { @@ -79,14 +81,18 @@ class IPFSServer { silent: true }); - this.httpServer = new IpfsHttpApi(this.node); + this.httpServer = new IpfsHttpApi(this.node) as IPFSHttpServer; await this.httpServer.start(); } async stop() { - await this.httpServer.stop(); - await this.node.stop(); + if (this.httpServer) { + await this.httpServer.stop(); + } + if (this.node) { + await this.node.stop(); + } } } diff --git a/src/chains/filecoin/filecoin/src/provider.ts b/src/chains/filecoin/filecoin/src/provider.ts index 2e589288ed..27e7a7f11d 100644 --- a/src/chains/filecoin/filecoin/src/provider.ts +++ b/src/chains/filecoin/filecoin/src/provider.ts @@ -1,5 +1,5 @@ import Emittery from "emittery"; -import { types, utils } from "@ganache/utils"; +import { PromiEvent, Subscription, types, utils } from "@ganache/utils"; import JsonRpc from "@ganache/utils/src/things/jsonrpc"; import FilecoinApi from "./api"; import GanacheSchema from "./schema"; @@ -7,25 +7,23 @@ import { Schema } from "@filecoin-shipyard/lotus-client-schema"; import Blockchain from "./blockchain"; import { FilecoinOptionsConfig, - FilecoinProviderOptions + FilecoinProviderOptions, + FilecoinInternalOptions } from "@ganache/filecoin-options"; +import cloneDeep from "lodash.clonedeep"; // Meant to mimic this provider: // https://github.com/filecoin-shipyard/js-lotus-client-provider-browser export default class FilecoinProvider - extends Emittery.Typed + extends Emittery.Typed<{}, "ready"> // Do I actually need this? `types.Provider` doesn't actually define anything behavior implements types.Provider { - #options: FilecoinProviderOptions; + #options: FilecoinInternalOptions; #api: FilecoinApi; #executor: utils.Executor; readonly blockchain: Blockchain; - // Used by the original Filecoin provider. Will mimic them for now. - // Not entirely sure they're needed. - #connectPromise: PromiseLike; - static readonly Schema: Schema = GanacheSchema; constructor(options: FilecoinProviderOptions = {}, executor: utils.Executor) { @@ -44,28 +42,76 @@ export default class FilecoinProvider this.#api = new FilecoinApi(this.blockchain); } + /** + * Returns the options, including defaults and generated, used to start Ganache. + */ + public getOptions() { + return cloneDeep(this.#options); + } + + /** + * Returns the unlocked accounts + */ + public getInitialAccounts() { + const accounts: Record< + string, + { unlocked: boolean; secretKey: string; balance: bigint } + > = {}; + accounts[this.blockchain.address.serialize()] = { + unlocked: true, + secretKey: this.blockchain.address.privateKey!, + balance: this.blockchain.balance.value + }; + return accounts; + } + async connect() { - if (this.#connectPromise) { - this.#connectPromise = new Promise(resolve => { - resolve(void 0); - }); - } - return this.#connectPromise; + await this.blockchain.waitForReady(); } - async send( + async send(payload: JsonRpc.Request) { + const result = await this._requestRaw(payload); + return result.value; + } + + async _requestRaw( payload: JsonRpc.Request ) { - // I'm not entirely sure why I need the `as [string]`... but it seems to work. - return this.#executor - .execute(this.#api, payload.method, payload.params as [string]) - .then(result => { - const promise = (result.value as unknown) as PromiseLike< - ReturnType - >; - - return promise.then(JSON.stringify).then(JSON.parse); + // The `as any` is needed here because of this hackery of appending the + // JSON `id` no longer fits within the strictly typed `execute` `params` + // argument + const result = await this.#executor.execute(this.#api, payload.method, [ + ...(payload.params || []), + payload.id + ] as any); + const promise = (result.value as unknown) as PromiseLike< + ReturnType + >; + + if (promise instanceof PromiEvent) { + promise.on("message", data => { + this.emit("message" as never, data as never); }); + + const value = await promise; + + if ( + typeof value === "object" && + typeof value.unsubscribe === "function" + ) { + // since the class instance gets ripped away, + // we can't use instanceof Subscription, so we + // just use an interface and check for the unsubscribe + // function 🤷 + const newPromiEvent = PromiEvent.resolve(value.id); + promise.on("message", data => { + newPromiEvent.emit("message" as never, data as never); + }); + return { value: newPromiEvent }; + } + } + + return { value: promise }; } async sendHttp() { @@ -76,8 +122,28 @@ export default class FilecoinProvider throw new Error("Method not supported (sendWs)"); } - async sendSubscription() { - throw new Error("Method not supported (sendSubscription)"); + // Reference implementation: https://git.io/JtO3H + async sendSubscription( + payload: JsonRpc.Request, + schemaMethod: { subscription?: boolean }, + subscriptionCallback: (data: any) => void + ) { + // I'm not entirely sure why I need the `as [string]`... but it seems to work. + const result = await this.#executor.execute(this.#api, payload.method, [ + ...(payload.params || []), + payload.id + ] as any); + const promiEvent = (result.value as unknown) as PromiEvent; + + if (promiEvent instanceof PromiEvent) { + promiEvent.on("message", data => { + subscriptionCallback(data); + }); + } + + const value = await promiEvent; + + return [value.unsubscribe, Promise.resolve(value.id.toString())]; } async receive() { diff --git a/src/chains/filecoin/filecoin/src/schema.ts b/src/chains/filecoin/filecoin/src/schema.ts index c182a579ee..7f412f38f6 100644 --- a/src/chains/filecoin/filecoin/src/schema.ts +++ b/src/chains/filecoin/filecoin/src/schema.ts @@ -1,18 +1,28 @@ import FilecoinApi from "./api"; -import { Schema } from "@filecoin-shipyard/lotus-client-schema"; +import LotusSchema, { Schema } from "@filecoin-shipyard/lotus-client-schema"; const GanacheSchema: Schema = { methods: {} } as Schema; +const combinedMethods = { + ...LotusSchema.mainnet.fullNode.methods, + ...LotusSchema.mainnet.storageMiner.methods, + ...LotusSchema.mainnet.gatewayApi.methods, + ...LotusSchema.mainnet.walletApi.methods, + ...LotusSchema.mainnet.workerApi.methods +}; + // Use the FilecoinAPI to create a schema object representing the functions supported. for (const methodName of Object.getOwnPropertyNames(FilecoinApi.prototype)) { - if (methodName == "constructor") { + if (!methodName.startsWith("Filecoin.")) { continue; } let schemaName = methodName.replace("Filecoin.", ""); - GanacheSchema.methods[schemaName] = {}; + GanacheSchema.methods[schemaName] = { + subscription: combinedMethods[schemaName].subscription === true + }; } export default GanacheSchema; diff --git a/src/chains/filecoin/filecoin/src/things/address.ts b/src/chains/filecoin/filecoin/src/things/address.ts index 3c86387b3c..938ae3674e 100644 --- a/src/chains/filecoin/filecoin/src/things/address.ts +++ b/src/chains/filecoin/filecoin/src/things/address.ts @@ -2,8 +2,11 @@ import { SerializableLiteral } from "./serializable-literal"; import blake from "blakejs"; import * as bls from "noble-bls12-381"; import base32 from "base32-encoding"; -import { StorageProposal } from "./storage-proposal"; +import { StartDealParams } from "./start-deal-params"; import cbor from "borc"; +import { utils } from "@ganache/utils"; + +// https://spec.filecoin.io/appendix/address/ interface AddressConfig { type: string; @@ -11,70 +14,64 @@ interface AddressConfig { enum AddressProtocol { ID, - // SECP256K1 represents the address SECP256K1 protocol. - SECP256K1, - // Actor represents the address Actor protocol. - Actor, - // BLS represents the address BLS protocol. - BLS, + SECP256K1, // Represents the address SECP256K1 protocol + Actor, // Represents the address Actor protocol + BLS, // Represents the address BLS protocol Unknown = 255 } enum AddressNetwork { Testnet = "t", - Mainnet = "m" + Mainnet = "f", + Unknown = "UNKNOWN" } class Address extends SerializableLiteral { get config() { - return { - defaultValue: literal => literal - }; + return {}; } - static readonly MAINNET_PREFIX = "f"; - static readonly TESTNET_PREFIX = "t"; - static readonly CHECKSUM_BYTES = 4; - readonly privateKey: string; - readonly protocol: AddressProtocol; - readonly network: AddressNetwork; - - constructor( - privateKey: string, - protocol: AddressProtocol = AddressProtocol.BLS, - network: AddressNetwork = AddressNetwork.Testnet - ) { - if (protocol != AddressProtocol.BLS) { - throw new Error( - "Protocol type not yet supported. Supported address protocols: BLS" - ); - } - - let address = Address.fromPrivateKey(privateKey, protocol, network); + #privateKey?: string; + get privateKey(): string | undefined { + return this.#privateKey; + } + get network(): AddressNetwork { + return Address.parseNetwork(this.value); + } + get protocol(): AddressProtocol { + return Address.parseProtocol(this.value); + } - // The serialized value is the address - super(address); + constructor(publicAddress: string, privateKey?: string) { + super(publicAddress); + this.#privateKey = privateKey; + } - this.privateKey = privateKey; - this.protocol = protocol; - this.network = network; + setPrivateKey(privateKey: string) { + this.#privateKey = privateKey; } - async signProposal(proposal: StorageProposal): Promise { - let serialized = proposal.serialize(); - let encoded = cbor.encode(serialized); + async signProposal(proposal: StartDealParams): Promise { + if (this.#privateKey) { + const serialized = proposal.serialize(); + const encoded = cbor.encode(serialized); - let signature = await bls.sign(encoded, this.privateKey); - return Buffer.from(signature); + const signature = await bls.sign(encoded, this.#privateKey); + return Buffer.from(signature); + } else { + throw new Error( + `Could not sign proposal with address ${this.value} due to not having the associated private key.` + ); + } } static fromPrivateKey( privateKey: string, - protocol: AddressProtocol, - network: AddressNetwork - ): string { + protocol: AddressProtocol = AddressProtocol.BLS, + network: AddressNetwork = AddressNetwork.Testnet + ): Address { if (protocol != AddressProtocol.BLS) { throw new Error( "Protocol type not yet supported. Supported address protocols: BLS" @@ -82,50 +79,97 @@ class Address extends SerializableLiteral { } // Reverse the key from little endian to big endian - let bigEndian = privateKey.match(/.{2}/g).reverse().join(""); + const regex = privateKey.match(/.{2}/g); + if (!regex) { + throw new Error( + `Could not create address from private key ${privateKey}` + ); + } + const bigEndian = regex.reverse().join(""); // Get the public key - let publicKey = Buffer.from(bls.getPublicKey(bigEndian)); + const publicKey = Buffer.from(bls.getPublicKey(bigEndian)); // Create a checksum using the blake2b algorithm - let checksumBuffer = Buffer.concat([Buffer.from([protocol]), publicKey]); - let checksum = blake.blake2b(checksumBuffer, null, Address.CHECKSUM_BYTES); + const checksumBuffer = Buffer.concat([Buffer.from([protocol]), publicKey]); + const checksum = blake.blake2b( + checksumBuffer, + null, + Address.CHECKSUM_BYTES + ); // Merge the public key and checksum - let checksummedPubKey = Buffer.concat([publicKey, checksum]); + const checksummedPubKey = Buffer.concat([publicKey, checksum]); // Use a custom alphabet to base32 encode the checksummed public key, // and prepend the network and protocol identifiers. - let customBase32Alpahbet = "abcdefghijklmnopqrstuvwxyz234567"; - let address = - network.toString() + - protocol.toString() + - base32.stringify(checksummedPubKey, customBase32Alpahbet); + const customBase32Alpahbet = "abcdefghijklmnopqrstuvwxyz234567"; + const address = `${network}${protocol}${base32.stringify( + checksummedPubKey, + customBase32Alpahbet + )}`; - return address; + return new Address(address, privateKey); } static random( - rng: () => number = Math.random, + rng: utils.RandomNumberGenerator = new utils.RandomNumberGenerator(), protocol: AddressProtocol = AddressProtocol.BLS, network: AddressNetwork = AddressNetwork.Testnet ): Address { // Note that this private key isn't cryptographically secure! // It uses insecure randomization! Don't use it in production! - let alphabet = "0123456789abcdef"; - let privateKey = "_" - .repeat(64) - .split("") - .map(() => alphabet[Math.floor(rng() * alphabet.length)]) - .join(""); - - return new Address(privateKey); + const privateKey = rng.getBuffer(32).toString("hex"); + + return Address.fromPrivateKey(privateKey, protocol, network); } // Note: This does not (yet) check for cryptographic validity! static isValid(value: string): boolean { return value.length == 86 && value.indexOf("t3") == 0; } + + static parseNetwork(publicAddress: string): AddressNetwork { + if (publicAddress.length < 1) { + return AddressNetwork.Unknown; + } + + switch (publicAddress.charAt(0)) { + case AddressNetwork.Mainnet: { + return AddressNetwork.Mainnet; + } + case AddressNetwork.Testnet: { + return AddressNetwork.Testnet; + } + default: { + return AddressNetwork.Unknown; + } + } + } + + static parseProtocol(publicAddress: string): AddressProtocol { + if (publicAddress.length < 2) { + return AddressProtocol.Unknown; + } + + switch (parseInt(publicAddress.charAt(1), 10)) { + case AddressProtocol.ID: { + return AddressProtocol.ID; + } + case AddressProtocol.BLS: { + return AddressProtocol.BLS; + } + case AddressProtocol.Actor: { + return AddressProtocol.Actor; + } + case AddressProtocol.SECP256K1: { + return AddressProtocol.SECP256K1; + } + default: { + return AddressProtocol.Unknown; + } + } + } } type SerializedAddress = string; diff --git a/src/chains/filecoin/filecoin/src/things/balance.ts b/src/chains/filecoin/filecoin/src/things/balance.ts index f3580ac13c..b3b037ac9e 100644 --- a/src/chains/filecoin/filecoin/src/things/balance.ts +++ b/src/chains/filecoin/filecoin/src/things/balance.ts @@ -1,25 +1,28 @@ -import { SerializableLiteral } from "./serializable-literal"; +import { LiteralDefinition, SerializableLiteral } from "./serializable-literal"; import BN from "bn.js"; interface BalanceConfig { - type: string; + type: bigint; } +// The smallest denomination of FIL is an attoFIL (10^-18 FIL) class Balance extends SerializableLiteral { - get config() { + get config(): LiteralDefinition { return { - defaultValue: literal => { - return literal || "500000000000000000000000"; - } + defaultValue: options => + options ? BigInt(options) : 500n * 1000000000000000000n }; } - sub(val: string | number): Balance { - return new Balance(new BN(this.value).sub(new BN(val)).toString(10)); + sub(val: string | number | bigint): Balance { + const newBalance = this.value - BigInt(val); + return new Balance(newBalance.toString(10)); } toFIL(): number { - return new BN(this.value).div(new BN(10).pow(new BN(21))).toNumber(); + return new BN(this.value.toString(10)) + .div(new BN(10).pow(new BN(18))) + .toNumber(); } } diff --git a/src/chains/filecoin/filecoin/src/things/beacon-entry.ts b/src/chains/filecoin/filecoin/src/things/beacon-entry.ts index 4181c0054f..5957476244 100644 --- a/src/chains/filecoin/filecoin/src/things/beacon-entry.ts +++ b/src/chains/filecoin/filecoin/src/things/beacon-entry.ts @@ -5,6 +5,8 @@ import { Definitions } from "./serializable-object"; +// https://pkg.go.dev/github.com/filecoin-project/lotus@v1.4.0/chain/types#BeaconEntry + interface BeaconEntryConfig { properties: { round: { @@ -13,7 +15,7 @@ interface BeaconEntryConfig { serializedName: "Round"; }; data: { - type: string; + type: Buffer; serializedType: string; serializedName: "Data"; }; @@ -26,18 +28,34 @@ class BeaconEntry get config(): Definitions { return { round: { + deserializedName: "round", serializedName: "Round", - defaultValue: 1321 + defaultValue: 0 }, data: { + deserializedName: "data", serializedName: "Data", - defaultValue: "qrwddPErWZxCQkTKvTkgKwxazkKZu2Q9nXHW1sPgW7I=" + defaultValue: literal => + typeof literal !== "undefined" + ? Buffer.from(literal, "base64") + : Buffer.from([0]) } }; } + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.round = super.initializeValue(this.config.round, options); + this.data = super.initializeValue(this.config.data, options); + } + round: number; - data: string; + data: Buffer; } type SerializedBeaconEntry = SerializedObject; diff --git a/src/chains/filecoin/filecoin/src/things/block-header.ts b/src/chains/filecoin/filecoin/src/things/block-header.ts new file mode 100644 index 0000000000..5fa781417e --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/block-header.ts @@ -0,0 +1,291 @@ +import { Ticket, SerializedTicket } from "./ticket"; +import { ElectionProof, SerializedElectionProof } from "./election-proof"; +import { BeaconEntry, SerializedBeaconEntry } from "./beacon-entry"; +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +import { PoStProof, SerializedPoStProof } from "./post-proof"; +import { RootCID, SerializedRootCID } from "./root-cid"; +import { CID } from "./cid"; +import cbor from "borc"; +import IPFSCid from "cids"; +import multihashing from "multihashing"; +import multicodec from "multicodec"; +import { SerializedSignature, Signature } from "./signature"; + +// https://pkg.go.dev/github.com/filecoin-project/lotus@v1.4.0/chain/types#BlockHeader + +interface BlockHeaderConfig { + properties: { + miner: { + type: string; // string until we can support more address types in Address + serializedType: string; + serializedName: "Miner"; + }; + ticket: { + type: Ticket; + serializedType: SerializedTicket; + serializedName: "Ticket"; + }; + electionProof: { + type: ElectionProof; + serializedType: SerializedElectionProof; + serializedName: "ElectionProof"; + }; + beaconEntries: { + type: Array; + serializedType: Array; + serializedName: "BeaconEntries"; + }; + winPoStProof: { + type: Array; + serializedType: Array; + serializedName: "WinPoStProof"; + }; + parents: { + type: Array; + serializedType: Array; + serializedName: "Parents"; + }; + parentWeight: { + type: bigint; + serializedType: string; + serializedName: "ParentWeight"; + }; + height: { + type: number; + serializedType: number; + serializedName: "Height"; + }; + parentStateRoot: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "ParentStateRoot"; + }; + parentMessageReceipts: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "ParentMessageReceipts"; + }; + messages: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "Messages"; + }; + blsAggregate: { + type: Signature; + serializedType: SerializedSignature; + serializedName: "BLSAggregate"; + }; + timestamp: { + type: number; + serializedType: number; + serializedName: "Timestamp"; + }; + blockSignature: { + type: Signature; + serializedType: SerializedSignature; + serializedName: "BlockSig"; + }; + forkSignaling: { + type: 0 | 1; + serializedType: 0 | 1; + serializedName: "ForkSignaling"; + }; + parentBaseFee: { + type: bigint; + serializedType: string; + serializedName: "ParentBaseFee"; + }; + }; +} + +class BlockHeader + extends SerializableObject + implements DeserializedObject { + get config(): Definitions { + return { + miner: { + deserializedName: "miner", + serializedName: "Miner", + defaultValue: "t01000" + }, + ticket: { + deserializedName: "ticket", + serializedName: "Ticket", + defaultValue: options => new Ticket(options) + }, + electionProof: { + deserializedName: "electionProof", + serializedName: "ElectionProof", + defaultValue: options => new ElectionProof(options) + }, + beaconEntries: { + deserializedName: "beaconEntries", + serializedName: "BeaconEntries", + defaultValue: options => + options ? options.map(entry => new BeaconEntry(entry)) : [] + }, + winPoStProof: { + deserializedName: "winPoStProof", + serializedName: "WinPoStProof", + defaultValue: options => + options ? options.map(proof => new PoStProof(proof)) : [] + }, + parents: { + deserializedName: "parents", + serializedName: "Parents", + defaultValue: options => + options ? options.map(parent => new RootCID(parent)) : [] + }, + parentWeight: { + deserializedName: "parentWeight", + serializedName: "ParentWeight", + defaultValue: literal => (literal ? BigInt(literal) : 0n) + }, + height: { + deserializedName: "height", + serializedName: "Height", + defaultValue: 0 + }, + parentStateRoot: { + deserializedName: "parentStateRoot", + serializedName: "ParentStateRoot", + defaultValue: options => new RootCID(options) + }, + parentMessageReceipts: { + deserializedName: "parentMessageReceipts", + serializedName: "ParentMessageReceipts", + defaultValue: options => new RootCID(options) + }, + messages: { + deserializedName: "messages", + serializedName: "Messages", + defaultValue: options => new RootCID(options) + }, + blsAggregate: { + deserializedName: "blsAggregate", + serializedName: "BLSAggregate", + defaultValue: options => new Signature(options) + }, + timestamp: { + deserializedName: "timestamp", + serializedName: "Timestamp", + defaultValue: () => { + return new Date().getTime() / 1000; + } + }, + blockSignature: { + deserializedName: "blockSignature", + serializedName: "BlockSig", + defaultValue: options => new Signature(options) + }, + forkSignaling: { + deserializedName: "forkSignaling", + serializedName: "ForkSignaling", + defaultValue: 0 + }, + parentBaseFee: { + deserializedName: "parentBaseFee", + serializedName: "ParentBaseFee", + defaultValue: literal => (literal ? BigInt(literal) : 0n) + } + }; + } + + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.miner = super.initializeValue(this.config.miner, options); + this.ticket = super.initializeValue(this.config.ticket, options); + this.electionProof = super.initializeValue( + this.config.electionProof, + options + ); + this.beaconEntries = super.initializeValue( + this.config.beaconEntries, + options + ); + this.winPoStProof = super.initializeValue( + this.config.winPoStProof, + options + ); + this.parents = super.initializeValue(this.config.parents, options); + this.parentWeight = super.initializeValue( + this.config.parentWeight, + options + ); + this.height = super.initializeValue(this.config.height, options); + this.parentStateRoot = super.initializeValue( + this.config.parentStateRoot, + options + ); + this.parentMessageReceipts = super.initializeValue( + this.config.parentMessageReceipts, + options + ); + this.messages = super.initializeValue(this.config.messages, options); + this.blsAggregate = super.initializeValue( + this.config.blsAggregate, + options + ); + this.timestamp = super.initializeValue(this.config.timestamp, options); + this.blockSignature = super.initializeValue( + this.config.blockSignature, + options + ); + this.forkSignaling = super.initializeValue( + this.config.forkSignaling, + options + ); + this.parentBaseFee = super.initializeValue( + this.config.parentBaseFee, + options + ); + } + + miner: string; + ticket: Ticket; + electionProof: ElectionProof; + beaconEntries: Array; + winPoStProof: Array; + parents: Array; + parentWeight: bigint; + height: number; + parentStateRoot: RootCID; + parentMessageReceipts: RootCID; + messages: RootCID; + blsAggregate: Signature; + /** + * Timestamp in seconds. Reference implementation: https://git.io/Jt3HJ. + */ + timestamp: number; + blockSignature: Signature; + forkSignaling: 0 | 1; + parentBaseFee: bigint; + + get cid(): CID { + // We could have used the ipld-dag-cbor package for the following, + // but it was async, which caused a number of issues during object construction. + const cborBlockHeader = cbor.encode(this.serialize()); + const multihash = multihashing(cborBlockHeader, "blake2b-256"); + const rawCid = new IPFSCid( + 1, + multicodec.print[multicodec.DAG_CBOR], + multihash + ); + + return new CID(rawCid.toString()); + } +} + +type SerializedBlockHeader = SerializedObject; + +export { BlockHeader, SerializedBlockHeader }; diff --git a/src/chains/filecoin/filecoin/src/things/channel-id.ts b/src/chains/filecoin/filecoin/src/things/channel-id.ts new file mode 100644 index 0000000000..733d7d7c73 --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/channel-id.ts @@ -0,0 +1,72 @@ +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; + +// https://pkg.go.dev/github.com/filecoin-project/go-data-transfer@v1.2.5#ChannelID + +type ChannelIDConfig = { + properties: { + initiator: { + type: string; + serializedType: string; + serializedName: "Initiator"; + }; + responder: { + type: string; + serializedType: string; + serializedName: "Responder"; + }; + id: { + type: number; + serializedType: number; + serializedName: "ID"; + }; + }; +}; + +class ChannelID + extends SerializableObject + implements DeserializedObject { + get config(): Definitions { + return { + initiator: { + deserializedName: "initiator", + serializedName: "Initiator", + defaultValue: "" + }, + responder: { + deserializedName: "responder", + serializedName: "Responder", + defaultValue: "" + }, + id: { + deserializedName: "id", + serializedName: "ID", + defaultValue: 0 + } + }; + } + + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.initiator = super.initializeValue(this.config.initiator, options); + this.responder = super.initializeValue(this.config.responder, options); + this.id = super.initializeValue(this.config.id, options); + } + + initiator: string; + responder: string; + id: number; +} + +type SerializedChannelID = SerializedObject; + +export { ChannelID, SerializedChannelID }; diff --git a/src/chains/filecoin/filecoin/src/things/cid.ts b/src/chains/filecoin/filecoin/src/things/cid.ts index d3fe62bb53..3faa4e2593 100644 --- a/src/chains/filecoin/filecoin/src/things/cid.ts +++ b/src/chains/filecoin/filecoin/src/things/cid.ts @@ -1,4 +1,8 @@ import { SerializableLiteral } from "./serializable-literal"; +import cbor from "borc"; +import IPFSCid from "cids"; +import multihashing from "multihashing"; +import multicodec from "multicodec"; interface CIDConfig { type: string; @@ -6,15 +10,25 @@ interface CIDConfig { class CID extends SerializableLiteral { get config() { - return { - required: true - }; + return {}; } // Note: This does not (yet) check for cryptographic validity! static isValid(value: string): boolean { return value.length >= 59 && value.indexOf("ba") == 0; } + + static nullCID(): CID { + const nilCbor = cbor.encode(0); // using null returns a not-nill cbor + const multihash = multihashing(nilCbor, "blake2b-256"); + const rawCid = new IPFSCid( + 1, + multicodec.print[multicodec.DAG_CBOR], + multihash + ); + + return new CID(rawCid.toString()); + } } type SerializedCID = string; diff --git a/src/chains/filecoin/filecoin/src/things/data-transfer-channel.ts b/src/chains/filecoin/filecoin/src/things/data-transfer-channel.ts new file mode 100644 index 0000000000..7dc624279a --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/data-transfer-channel.ts @@ -0,0 +1,146 @@ +import { RootCID, SerializedRootCID } from "./root-cid"; +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; + +// https://pkg.go.dev/github.com/filecoin-project/lotus@v1.4.0/api#DataTransferChannel + +type DataTransferChannelConfig = { + properties: { + transferId: { + type: number; + serializedType: number; + serializedName: "TransferID"; + }; + status: { + type: number; + serializedType: number; + serializedName: "Status"; + }; + baseCID: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "BaseCID"; + }; + isInitiator: { + type: boolean; + serializedType: boolean; + serializedName: "IsInitiator"; + }; + isSender: { + type: boolean; + serializedType: boolean; + serializedName: "IsSender"; + }; + voucher: { + type: string; + serializedType: string; + serializedName: "Voucher"; + }; + message: { + type: string; + serializedType: string; + serializedName: "Message"; + }; + otherPeer: { + type: string; + serializedType: string; + serializedName: "OtherPeer"; + }; + transferred: { + type: number; + serializedType: number; + serializedName: "Transferred"; + }; + }; +}; + +class DataTransferChannel + extends SerializableObject + implements DeserializedObject { + get config(): Definitions { + return { + transferId: { + deserializedName: "transferId", + serializedName: "TransferID", + defaultValue: 0 + }, + status: { + deserializedName: "status", + serializedName: "Status", + defaultValue: 0 + }, + baseCID: { + deserializedName: "baseCID", + serializedName: "BaseCID", + defaultValue: options => + options ? new RootCID(options) : new RootCID({ "/": "Unknown" }) + }, + isInitiator: { + deserializedName: "isInitiator", + serializedName: "IsInitiator", + defaultValue: false + }, + isSender: { + deserializedName: "isSender", + serializedName: "IsSender", + defaultValue: false + }, + voucher: { + deserializedName: "voucher", + serializedName: "Voucher", + defaultValue: "" + }, + message: { + deserializedName: "message", + serializedName: "Message", + defaultValue: "" + }, + otherPeer: { + deserializedName: "otherPeer", + serializedName: "OtherPeer", + defaultValue: "" + }, + transferred: { + deserializedName: "transferred", + serializedName: "Transferred", + defaultValue: 0 + } + }; + } + + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.transferId = super.initializeValue(this.config.transferId, options); + this.status = super.initializeValue(this.config.status, options); + this.baseCID = super.initializeValue(this.config.baseCID, options); + this.isInitiator = super.initializeValue(this.config.isInitiator, options); + this.isSender = super.initializeValue(this.config.isSender, options); + this.voucher = super.initializeValue(this.config.voucher, options); + this.message = super.initializeValue(this.config.message, options); + this.otherPeer = super.initializeValue(this.config.otherPeer, options); + this.transferred = super.initializeValue(this.config.transferred, options); + } + + transferId: number; + status: number; + baseCID: RootCID; + isInitiator: boolean; + isSender: boolean; + voucher: string; + message: string; + otherPeer: string; + transferred: number; +} + +type SerializedDataTransferChannel = SerializedObject; + +export { DataTransferChannel, SerializedDataTransferChannel }; diff --git a/src/chains/filecoin/filecoin/src/things/deal-info.ts b/src/chains/filecoin/filecoin/src/things/deal-info.ts new file mode 100644 index 0000000000..4a7dd9c8d5 --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/deal-info.ts @@ -0,0 +1,234 @@ +import { RootCID, SerializedRootCID } from "./root-cid"; +import { + StorageDealStatus, + nextSuccessfulState +} from "../types/storage-deal-status"; +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +import { + SerializedStorageMarketDataRef, + StorageMarketDataRef +} from "./storage-market-data-ref"; +import { ChannelID, SerializedChannelID } from "./channel-id"; +import { + DataTransferChannel, + SerializedDataTransferChannel +} from "./data-transfer-channel"; + +// https://pkg.go.dev/github.com/filecoin-project/lotus@v1.4.0/api#DealInfo + +type DealInfoConfig = { + properties: { + proposalCid: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "ProposalCid"; + }; + state: { + type: StorageDealStatus; + serializedType: StorageDealStatus; // Remember: Enums are numbers at runtime!, + serializedName: "State"; + }; + message: { + type: string; + serializedType: string; + serializedName: "Message"; + }; + provider: { + type: string; // using string until we can support more address types in Address + serializedType: string; + serializedName: "Provider"; + }; + dataRef: { + type: StorageMarketDataRef; + serializedType: SerializedStorageMarketDataRef; + serializedName: "DataRef"; + }; + pieceCid: { + type: RootCID | null; + serializedType: SerializedRootCID | null; + serializedName: "PieceCID"; + }; + size: { + type: number; + serializedType: number; + serializedName: "Size"; + }; + pricePerEpoch: { + type: bigint; + serializedType: string; + serializedName: "PricePerEpoch"; + }; + duration: { + type: number; + serializedType: number; + serializedName: "Duration"; + }; + dealId: { + type: number; + serializedType: number; + serializedName: "DealID"; + }; + creationTime: { + type: Date; + serializedType: string; + serializedName: "CreationTime"; + }; + verified: { + type: boolean; + serializedType: boolean; + serializedName: "Verified"; + }; + transferChannelId: { + type: ChannelID; + serializedType: SerializedChannelID; + serializedName: "TransferChannelID"; + }; + dataTransfer: { + type: DataTransferChannel; + serializedType: SerializedDataTransferChannel; + serializedName: "DataTransfer"; + }; + }; +}; + +class DealInfo + extends SerializableObject + implements DeserializedObject { + get config(): Definitions { + return { + proposalCid: { + deserializedName: "proposalCid", + serializedName: "ProposalCid", + defaultValue: options => new RootCID(options) + }, + state: { + deserializedName: "state", + serializedName: "State", + defaultValue: StorageDealStatus.Unknown + }, + message: { + deserializedName: "message", + serializedName: "Message", + defaultValue: "" + }, + provider: { + deserializedName: "provider", + serializedName: "Provider", + defaultValue: "t01000" + }, + dataRef: { + deserializedName: "dataRef", + serializedName: "DataRef", + defaultValue: options => new StorageMarketDataRef(options) + }, + pieceCid: { + deserializedName: "pieceCid", + serializedName: "PieceCID", + defaultValue: options => (options ? new RootCID(options) : null) + }, + size: { + deserializedName: "size", + serializedName: "Size", + defaultValue: 0 + }, + pricePerEpoch: { + deserializedName: "pricePerEpoch", + serializedName: "PricePerEpoch", + defaultValue: literal => (literal ? BigInt(literal) : 0n) + }, + duration: { + deserializedName: "duration", + serializedName: "Duration", + defaultValue: 0 + }, + dealId: { + deserializedName: "dealId", + serializedName: "DealID", + defaultValue: 0 + }, + creationTime: { + deserializedName: "creationTime", + serializedName: "CreationTime", + defaultValue: new Date() + }, + verified: { + deserializedName: "verified", + serializedName: "Verified", + defaultValue: false + }, + transferChannelId: { + deserializedName: "transferChannelId", + serializedName: "TransferChannelID", + defaultValue: options => new ChannelID(options) + }, + dataTransfer: { + deserializedName: "dataTransfer", + serializedName: "DataTransfer", + defaultValue: options => new DataTransferChannel(options) + } + }; + } + + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.proposalCid = super.initializeValue(this.config.proposalCid, options); + this.state = super.initializeValue(this.config.state, options); + this.message = super.initializeValue(this.config.message, options); + this.provider = super.initializeValue(this.config.provider, options); + this.dataRef = super.initializeValue(this.config.dataRef, options); + this.pieceCid = super.initializeValue(this.config.pieceCid, options); + this.size = super.initializeValue(this.config.size, options); + this.pricePerEpoch = super.initializeValue( + this.config.pricePerEpoch, + options + ); + this.duration = super.initializeValue(this.config.duration, options); + this.dealId = super.initializeValue(this.config.dealId, options); + this.creationTime = super.initializeValue( + this.config.creationTime, + options + ); + this.verified = super.initializeValue(this.config.verified, options); + this.transferChannelId = super.initializeValue( + this.config.transferChannelId, + options + ); + this.dataTransfer = super.initializeValue( + this.config.dataTransfer, + options + ); + } + + proposalCid: RootCID; + state: StorageDealStatus; + message: string; + provider: string; + dataRef: StorageMarketDataRef; + pieceCid: RootCID | null; + size: number; + pricePerEpoch: bigint; + duration: number; + dealId: number; + creationTime: Date; + verified: boolean; + transferChannelId: ChannelID; + dataTransfer: DataTransferChannel; + + advanceState() { + this.state = nextSuccessfulState[this.state]; + } +} + +type SerializedDealInfo = SerializedObject; + +export { DealInfo, SerializedDealInfo }; diff --git a/src/chains/filecoin/filecoin/src/things/election-proof.ts b/src/chains/filecoin/filecoin/src/things/election-proof.ts index 517d5416de..bd9100c095 100644 --- a/src/chains/filecoin/filecoin/src/things/election-proof.ts +++ b/src/chains/filecoin/filecoin/src/things/election-proof.ts @@ -5,10 +5,17 @@ import { Definitions } from "./serializable-object"; +// https://pkg.go.dev/github.com/filecoin-project/lotus@v1.4.0/chain/types#ElectionProof + interface ElectionProofConfig { properties: { + winCount: { + type: number; + serializedType: number; + serializedName: "WinCount"; + }; vrfProof: { - type: string; + type: Buffer; serializedType: string; serializedName: "VRFProof"; }; @@ -20,16 +27,35 @@ class ElectionProof implements DeserializedObject { get config(): Definitions { return { + winCount: { + deserializedName: "winCount", + serializedName: "WinCount", + defaultValue: 1 + }, vrfProof: { + deserializedName: "vrfProof", serializedName: "VRFProof", - defaultValue: () => { - return "kQHqldOpdnmexjOh8KwzR6kjSGHAD6tWWM9DpTgf1e/FuxZXwB6lSXg9rlVyMk1OFbRbOOqvbHL5ZER/HTD3a3d3DTlmJ6T8H+oAqVTkh64hdoX2QTyL9EHymMIpgTKX"; - } + defaultValue: literal => + typeof literal !== "undefined" + ? Buffer.from(literal, "base64") + : Buffer.from([0]) } }; } - vrfProof: string; + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.winCount = super.initializeValue(this.config.winCount, options); + this.vrfProof = super.initializeValue(this.config.vrfProof, options); + } + + winCount: number; + vrfProof: Buffer; } type SerializedElectionProof = SerializedObject; diff --git a/src/chains/filecoin/filecoin/src/things/head-change.ts b/src/chains/filecoin/filecoin/src/things/head-change.ts new file mode 100644 index 0000000000..543f7e2926 --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/head-change.ts @@ -0,0 +1,68 @@ +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +import { Tipset, SerializedTipset } from "./tipset"; + +// https://pkg.go.dev/github.com/filecoin-project/lotus@v1.4.0/api#HeadChange + +interface HeadChangeConfig { + properties: { + type: { + type: string; + serializedType: string; + serializedName: "Type"; + }; + val: { + type: Tipset; + serializedType: SerializedTipset; + serializedName: "Val"; + }; + }; +} + +class HeadChange + extends SerializableObject + implements DeserializedObject { + get config(): Definitions { + return { + type: { + deserializedName: "type", + serializedName: "Type", + defaultValue: options => options || HeadChangeType.HCCurrent + }, + val: { + deserializedName: "val", + serializedName: "Val", + defaultValue: options => new Tipset(options) + } + }; + } + + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.type = super.initializeValue(this.config.type, options); + this.val = super.initializeValue(this.config.val, options); + } + + type: string; + val: Tipset; +} + +type SerializedHeadChange = SerializedObject; + +// Retrieved these from https://git.io/Jtvke +export enum HeadChangeType { + HCRevert = "revert", + HCApply = "apply", + HCCurrent = "current" +} + +export { HeadChange, SerializedHeadChange }; diff --git a/src/chains/filecoin/filecoin/src/things/miner.ts b/src/chains/filecoin/filecoin/src/things/miner.ts deleted file mode 100644 index b1598e7535..0000000000 --- a/src/chains/filecoin/filecoin/src/things/miner.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { SerializableLiteral } from "./serializable-literal"; - -interface MinerConfig { - type: string; -} - -class Miner extends SerializableLiteral { - get config() { - return { - defaultValue: literal => { - return literal || "t01000"; - } - }; - } -} - -type SerializedMiner = string; - -export { Miner, SerializedMiner }; diff --git a/src/chains/filecoin/filecoin/src/things/post-proof.ts b/src/chains/filecoin/filecoin/src/things/post-proof.ts new file mode 100644 index 0000000000..2dbf14a793 --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/post-proof.ts @@ -0,0 +1,63 @@ +import { + SerializableObject, + DeserializedObject, + SerializedObject, + Definitions +} from "./serializable-object"; + +// https://pkg.go.dev/github.com/filecoin-project/specs-actors@v0.9.13/actors/runtime/proof#PoStProof + +interface PoStProofConfig { + properties: { + postProof: { + type: number; + serializedType: number; + serializedName: "PoStProof"; + }; + proofBytes: { + type: Buffer; + serializedType: string; + serializedName: "ProofBytes"; + }; + }; +} + +class PoStProof + extends SerializableObject + implements DeserializedObject { + get config(): Definitions { + return { + postProof: { + deserializedName: "postProof", + serializedName: "PoStProof", + defaultValue: 0 + }, + proofBytes: { + deserializedName: "proofBytes", + serializedName: "ProofBytes", + defaultValue: literal => + typeof literal !== "undefined" + ? Buffer.from(literal, "base64") + : Buffer.from([0]) + } + }; + } + + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.postProof = super.initializeValue(this.config.postProof, options); + this.proofBytes = super.initializeValue(this.config.proofBytes, options); + } + + postProof: number; + proofBytes: Buffer; +} + +type SerializedPoStProof = SerializedObject; + +export { PoStProof, SerializedPoStProof }; diff --git a/src/chains/filecoin/filecoin/src/things/query-offer.ts b/src/chains/filecoin/filecoin/src/things/query-offer.ts new file mode 100644 index 0000000000..3e5a91b590 --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/query-offer.ts @@ -0,0 +1,164 @@ +import { RootCID, SerializedRootCID } from "./root-cid"; +import { + SerializableObject, + SerializedObject, + DeserializedObject, + Definitions +} from "./serializable-object"; +import { RetrievalPeer, SerializedRetrievalPeer } from "./retrieval-peer"; + +// https://pkg.go.dev/github.com/filecoin-project/lotus@v1.4.0/api#QueryOffer + +type QueryOfferConfig = { + properties: { + err: { + type: string; + serializedType: string; + serializedName: "Err"; + }; + root: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "Root"; + }; + piece: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "Piece"; + }; + size: { + type: number; + serializedType: number; + serializedName: "Size"; + }; + minPrice: { + type: bigint; + serializedType: string; + serializedName: "MinPrice"; + }; + unsealPrice: { + type: bigint; + serializedType: string; + serializedName: "UnsealPrice"; + }; + paymentInterval: { + type: number; + serializedType: number; + serializedName: "PaymentInterval"; + }; + paymentIntervalIncrease: { + type: number; + serializedType: number; + serializedName: "PaymentIntervalIncrease"; + }; + miner: { + type: string; // using string until we can support more address types in Address + serializedType: string; + serializedName: "Miner"; + }; + minerPeer: { + type: RetrievalPeer; + serializedType: SerializedRetrievalPeer; + serializedName: "MinerPeer"; + }; + }; +}; + +class QueryOffer + extends SerializableObject + implements DeserializedObject { + get config(): Definitions { + return { + err: { + deserializedName: "err", + serializedName: "Err", + defaultValue: "" + }, + root: { + deserializedName: "root", + serializedName: "Root", + defaultValue: options => new RootCID(options) + }, + piece: { + deserializedName: "piece", + serializedName: "Piece", + defaultValue: options => new RootCID(options) + }, + size: { + deserializedName: "size", + serializedName: "Size", + defaultValue: 0 + }, + minPrice: { + deserializedName: "minPrice", + serializedName: "MinPrice", + defaultValue: literal => (literal ? BigInt(literal) : 0n) + }, + unsealPrice: { + deserializedName: "unsealPrice", + serializedName: "UnsealPrice", + defaultValue: literal => (literal ? BigInt(literal) : 0n) + }, + paymentInterval: { + deserializedName: "paymentInterval", + serializedName: "PaymentInterval", + defaultValue: 1048576 + }, + paymentIntervalIncrease: { + deserializedName: "paymentIntervalIncrease", + serializedName: "PaymentIntervalIncrease", + defaultValue: 1048576 + }, + miner: { + deserializedName: "miner", + serializedName: "Miner", + defaultValue: "t01000" + }, + minerPeer: { + deserializedName: "minerPeer", + serializedName: "MinerPeer", + defaultValue: options => new RetrievalPeer(options) + } + }; + } + + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.err = super.initializeValue(this.config.err, options); + this.root = super.initializeValue(this.config.root, options); + this.piece = super.initializeValue(this.config.piece, options); + this.size = super.initializeValue(this.config.size, options); + this.minPrice = super.initializeValue(this.config.minPrice, options); + this.unsealPrice = super.initializeValue(this.config.unsealPrice, options); + this.paymentInterval = super.initializeValue( + this.config.paymentInterval, + options + ); + this.paymentIntervalIncrease = super.initializeValue( + this.config.paymentIntervalIncrease, + options + ); + this.miner = super.initializeValue(this.config.miner, options); + this.minerPeer = super.initializeValue(this.config.minerPeer, options); + } + + err: string; + root: RootCID; + piece: RootCID; + size: number; + minPrice: bigint; + unsealPrice: bigint; + paymentInterval: number; + paymentIntervalIncrease: number; + miner: string; + minerPeer: RetrievalPeer; +} + +type SerializedQueryOffer = SerializedObject; + +export { QueryOffer, SerializedQueryOffer }; diff --git a/src/chains/filecoin/filecoin/src/things/retrieval-order.ts b/src/chains/filecoin/filecoin/src/things/retrieval-order.ts new file mode 100644 index 0000000000..d344fd26f4 --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/retrieval-order.ts @@ -0,0 +1,164 @@ +import { RootCID, SerializedRootCID } from "./root-cid"; +import { + SerializableObject, + SerializedObject, + DeserializedObject, + Definitions +} from "./serializable-object"; +import { RetrievalPeer, SerializedRetrievalPeer } from "./retrieval-peer"; + +// https://pkg.go.dev/github.com/filecoin-project/lotus@v1.4.0/api#RetrievalOrder + +type RetrievalOrderConfig = { + properties: { + root: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "Root"; + }; + piece: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "Piece"; + }; + size: { + type: number; + serializedType: number; + serializedName: "Size"; + }; + total: { + type: bigint; + serializedType: string; + serializedName: "Total"; + }; + unsealPrice: { + type: bigint; + serializedType: string; + serializedName: "UnsealPrice"; + }; + paymentInterval: { + type: number; + serializedType: number; + serializedName: "PaymentInterval"; + }; + paymentIntervalIncrease: { + type: number; + serializedType: number; + serializedName: "PaymentIntervalIncrease"; + }; + client: { + type: string; // using string until we can support more address types in Address + serializedType: string; + serializedName: "Client"; + }; + miner: { + type: string; // using string until we can support more address types in Address + serializedType: string; + serializedName: "Miner"; + }; + minerPeer: { + type: RetrievalPeer; + serializedType: SerializedRetrievalPeer; + serializedName: "MinerPeer"; + }; + }; +}; + +class RetrievalOrder + extends SerializableObject + implements DeserializedObject { + get config(): Definitions { + return { + root: { + deserializedName: "root", + serializedName: "Root", + defaultValue: options => new RootCID(options) + }, + piece: { + deserializedName: "piece", + serializedName: "Piece", + defaultValue: options => new RootCID(options) + }, + size: { + deserializedName: "size", + serializedName: "Size", + defaultValue: 0 + }, + total: { + deserializedName: "total", + serializedName: "Total", + defaultValue: literal => (literal ? BigInt(literal) : 0n) + }, + unsealPrice: { + deserializedName: "unsealPrice", + serializedName: "UnsealPrice", + defaultValue: literal => (literal ? BigInt(literal) : 0n) + }, + paymentInterval: { + deserializedName: "paymentInterval", + serializedName: "PaymentInterval", + defaultValue: 1048576 + }, + paymentIntervalIncrease: { + deserializedName: "paymentIntervalIncrease", + serializedName: "PaymentIntervalIncrease", + defaultValue: 1048576 + }, + client: { + deserializedName: "client", + serializedName: "Client", + defaultValue: "t02000" + }, + miner: { + deserializedName: "miner", + serializedName: "Miner", + defaultValue: "t01000" + }, + minerPeer: { + deserializedName: "minerPeer", + serializedName: "MinerPeer", + defaultValue: options => new RetrievalPeer(options) + } + }; + } + + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.root = super.initializeValue(this.config.root, options); + this.piece = super.initializeValue(this.config.piece, options); + this.size = super.initializeValue(this.config.size, options); + this.total = super.initializeValue(this.config.total, options); + this.unsealPrice = super.initializeValue(this.config.unsealPrice, options); + this.paymentInterval = super.initializeValue( + this.config.paymentInterval, + options + ); + this.paymentIntervalIncrease = super.initializeValue( + this.config.paymentIntervalIncrease, + options + ); + this.client = super.initializeValue(this.config.client, options); + this.miner = super.initializeValue(this.config.miner, options); + this.minerPeer = super.initializeValue(this.config.minerPeer, options); + } + + root: RootCID; + piece: RootCID; + size: number; + total: bigint; + unsealPrice: bigint; + paymentInterval: number; + paymentIntervalIncrease: number; + client: string; + miner: string; + minerPeer: RetrievalPeer; +} + +type SerializedRetrievalOrder = SerializedObject; + +export { RetrievalOrder, SerializedRetrievalOrder }; diff --git a/src/chains/filecoin/filecoin/src/things/retrieval-peer.ts b/src/chains/filecoin/filecoin/src/things/retrieval-peer.ts new file mode 100644 index 0000000000..6249672645 --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/retrieval-peer.ts @@ -0,0 +1,73 @@ +import { + SerializableObject, + SerializedObject, + DeserializedObject, + Definitions +} from "./serializable-object"; +import { RootCID, SerializedRootCID } from "./root-cid"; + +// https://pkg.go.dev/github.com/filecoin-project/go-fil-markets@v1.1.1/retrievalmarket#RetrievalPeer + +type RetrievalPeerConfig = { + properties: { + address: { + type: string; // using string until we can support more address types in Address + serializedType: string; + serializedName: "Address"; + }; + id: { + type: string; + serializedType: string; + serializedName: "ID"; + }; + pieceCID: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "PieceCID"; + }; + }; +}; + +class RetrievalPeer + extends SerializableObject + implements DeserializedObject { + get config(): Definitions { + return { + address: { + deserializedName: "address", + serializedName: "Address", + defaultValue: "t01000" + }, + id: { + deserializedName: "id", + serializedName: "ID", + defaultValue: "0" + }, + pieceCID: { + deserializedName: "pieceCID", + serializedName: "PieceCID", + defaultValue: options => new RootCID(options) + } + }; + } + + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.address = super.initializeValue(this.config.address, options); + this.id = super.initializeValue(this.config.id, options); + this.pieceCID = super.initializeValue(this.config.pieceCID, options); + } + + address: string; + id: string; + pieceCID: RootCID; +} + +type SerializedRetrievalPeer = SerializedObject; + +export { RetrievalPeer, SerializedRetrievalPeer }; diff --git a/src/chains/filecoin/filecoin/src/things/root-cid.ts b/src/chains/filecoin/filecoin/src/things/root-cid.ts index 3b8309bb19..dbde268a95 100644 --- a/src/chains/filecoin/filecoin/src/things/root-cid.ts +++ b/src/chains/filecoin/filecoin/src/things/root-cid.ts @@ -8,7 +8,7 @@ import { interface RootCIDConfig { properties: { - "/": { + root: { type: CID; serializedType: SerializedCID; serializedName: "/"; @@ -21,20 +21,31 @@ class RootCID implements DeserializedObject { get config(): Definitions { return { - "/": { + root: { + deserializedName: "root", serializedName: "/", defaultValue: options => { - return new CID(options); + return options ? new CID(options) : CID.nullCID(); } } }; } + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.root = super.initializeValue(this.config.root, options); + } + asPath(): string { - return "/" + this["/"].value; + return "/" + this.root.value; } - "/": CID; + root: CID; } type SerializedRootCID = SerializedObject; diff --git a/src/chains/filecoin/filecoin/src/things/serializable-literal.ts b/src/chains/filecoin/filecoin/src/things/serializable-literal.ts index 08aa5b0129..97c379c012 100644 --- a/src/chains/filecoin/filecoin/src/things/serializable-literal.ts +++ b/src/chains/filecoin/filecoin/src/things/serializable-literal.ts @@ -1,55 +1,58 @@ import { Serializable } from "./serializable-object"; -import { config } from "yargs"; type BaseConfig = { - type: any; + type: number | string | Buffer | bigint | null; }; type Literal = C["type"]; -type DefaultValue = D | ((options: D) => D); +type SerializedLiteral = C["type"] extends bigint | Buffer + ? string + : Literal; + +type DefaultValue = D | ((options: S | undefined) => D); type LiteralDefinition = { - defaultValue?: DefaultValue>; - required?: boolean; + defaultValue?: DefaultValue, Literal>; }; abstract class SerializableLiteral - implements Serializable> { + implements Serializable> { protected abstract get config(): LiteralDefinition; value: Literal; - constructor(literal?: Literal) { - this.initialize(literal); + constructor(literal?: SerializedLiteral) { + this.value = this.initialize(literal); } - private initialize(literal: Literal) { - if (this.config.defaultValue && literal === undefined) { - const def = this.config.defaultValue; - - if (typeof def == "function") { - this.value = (def as any)(literal); - } else { - this.value = def; - } + private initialize(literal?: SerializedLiteral): Literal { + const def = this.config.defaultValue; + if (typeof def === "function") { + return def(literal); + } else if (typeof literal !== "undefined") { + return literal; + } else if (typeof def !== "function" && typeof def !== "undefined") { + return def; } else { - this.value = literal; - } - - if (this.config.required && typeof this.value == "undefined") { throw new Error(`A value is required for class ${this.constructor.name}`); } } - serialize(): Literal { - return this.value; + serialize(): SerializedLiteral { + if (typeof this.value === "bigint") { + return this.value.toString(10) as SerializedLiteral; + } else if (Buffer.isBuffer(this.value)) { + return this.value.toString("base64") as SerializedLiteral; + } else { + return this.value as SerializedLiteral; + } } equals(obj: Serializable>): boolean { let a: Literal = this.serialize(); let b: Literal = obj.serialize(); - return a == b; + return a === b; } } diff --git a/src/chains/filecoin/filecoin/src/things/serializable-object.ts b/src/chains/filecoin/filecoin/src/things/serializable-object.ts index 88567c44bf..40e33cf2b2 100644 --- a/src/chains/filecoin/filecoin/src/things/serializable-object.ts +++ b/src/chains/filecoin/filecoin/src/things/serializable-object.ts @@ -74,11 +74,11 @@ type SerializedObject = FlattenUnion< type DefaultValue = // A default value can be: | D // the expected type - | ((options: S) => D); // a fn that takes in a serialized object and returns the type + | ((options?: S) => D); // a fn that takes in a serialized object and returns the type type Definition> = { + deserializedName: N; serializedName: SerializedPropertyName; - defaultValue?: DefaultValue, SerializedPropertyType>; - required?: boolean; + defaultValue: DefaultValue, SerializedPropertyType>; }; // purpose of this type is to have a value type Definitions = { @@ -106,60 +106,53 @@ abstract class SerializableObject // The constructor can take in a serialized object, or a deserialized one. // Note that SerializableObject is the deserialized object in value land. - constructor( - options?: Partial> | Partial> - ) { - this.initialize(options); - } - private initialize( - options: Partial> | Partial> - ): void { + initializeValue>( + valueConfig: Definition, + options?: Partial> | Partial> + ): PropertyType { if (!options) { - options = {} as SerializedObject; + options = {}; } - for (const [deserializedName, { serializedName }] of Object.entries( - this.config - )) { - let def = this.config[deserializedName].defaultValue; - let value: any; - - // We don't know whether we were passed a serialized object or a - // deserialized one, so let's look for both keys. - if (typeof options[deserializedName] != "undefined") { - value = options[deserializedName]; - } else { - value = options[serializedName]; - } - - // Ensure everything is serialized after this point, - // as defaultValue functions expect serialized data - value = this.serializeValue(value); - - this[deserializedName] = value; - - if (typeof def == "function") { - // TODO: why the `(def as any)` here? - this[deserializedName] = (def as any)(value); - } else if (typeof value === "undefined") { - this[deserializedName] = def; - } - - if ( - this.config[deserializedName].required && - typeof this[deserializedName] == "undefined" - ) { - throw new Error( - `${deserializedName} is required for class ${this.constructor.name}` - ); - } + const def = valueConfig.defaultValue; + + // We don't know whether we were passed a serialized object or a + // deserialized one, so let's look for both keys. + const deserializedInput: PropertyType | undefined = (options as any)[ + valueConfig.deserializedName + ]; + const serializedInput: + | SerializedPropertyType + | undefined = (options as any)[valueConfig.serializedName]; + + if (typeof deserializedInput !== "undefined") { + return deserializedInput; + } else if (typeof def === "function") { + const typedDef = def as ( + options?: SerializedPropertyType + ) => PropertyType; + return typedDef(serializedInput); + } else if (typeof serializedInput !== "undefined") { + return serializedInput; + } else if (typeof def !== "function") { + return def; + } else { + throw new Error( + `A value is required for ${this.constructor.name}.${valueConfig.deserializedName}` + ); } } private serializeValue(value: any) { let returnVal: any = value; - if ( + if (typeof value === "bigint") { + returnVal = value.toString(10); + } else if (Buffer.isBuffer(value)) { + // golang serializes "byte[]" with base-64 encoding + // https://golang.org/src/encoding/json/encode.go?s=6458:6501#L55 + returnVal = value.toString("base64"); + } else if ( value instanceof SerializableObject || value instanceof SerializableLiteral ) { @@ -176,8 +169,8 @@ abstract class SerializableObject for (const [deserializedName, { serializedName }] of Object.entries( this.config )) { - let value = this[deserializedName]; - returnVal[serializedName] = this.serializeValue(value); + let value = (this as any)[deserializedName]; + (returnVal as any)[serializedName] = this.serializeValue(value); } return returnVal; diff --git a/src/chains/filecoin/filecoin/src/things/sig-type.ts b/src/chains/filecoin/filecoin/src/things/sig-type.ts new file mode 100644 index 0000000000..eafdd22c5c --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/sig-type.ts @@ -0,0 +1,7 @@ +// Reference implementation: https://git.io/JtsJc +export enum SigType { + SigTypeUnknown = 255, + + SigTypeSecp256k1 = 1, // I don't fully understand `iota`, but I put this through a golang compiler and it said it's 1 + SigTypeBLS // Purposely not explicitly stating to coincide with reference implementation (which is autoincrement) +} diff --git a/src/chains/filecoin/filecoin/src/things/signature.ts b/src/chains/filecoin/filecoin/src/things/signature.ts new file mode 100644 index 0000000000..34a5450f0d --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/signature.ts @@ -0,0 +1,64 @@ +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +import { SigType } from "./sig-type"; + +// https://pkg.go.dev/github.com/filecoin-project/go-state-types@v0.0.0-20201203022337-7cab7f0d4bfb/crypto#Signature + +interface SignatureConfig { + properties: { + type: { + type: number; + serializedType: number; + serializedName: "Type"; + }; + data: { + type: Buffer; + serializedType: string; + serializedName: "Data"; + }; + }; +} + +class Signature + extends SerializableObject + implements DeserializedObject { + get config(): Definitions { + return { + type: { + deserializedName: "type", + serializedName: "Type", + defaultValue: SigType.SigTypeUnknown + }, + data: { + deserializedName: "data", + serializedName: "Data", + defaultValue: literal => + typeof literal !== "undefined" + ? Buffer.from(literal, "base64") + : Buffer.from([0]) + } + }; + } + + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.type = super.initializeValue(this.config.type, options); + this.data = super.initializeValue(this.config.data, options); + } + + type: number; + data: Buffer; +} + +type SerializedSignature = SerializedObject; + +export { Signature, SerializedSignature }; diff --git a/src/chains/filecoin/filecoin/src/things/start-deal-params.ts b/src/chains/filecoin/filecoin/src/things/start-deal-params.ts new file mode 100644 index 0000000000..4eed2d16a2 --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/start-deal-params.ts @@ -0,0 +1,164 @@ +import { + StorageMarketDataRef, + SerializedStorageMarketDataRef +} from "./storage-market-data-ref"; +import { Address, SerializedAddress } from "./address"; +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; + +// https://pkg.go.dev/github.com/filecoin-project/lotus@v1.4.0/api#StartDealParams + +type StartDealParamsConfig = { + properties: { + data: { + type: StorageMarketDataRef; + serializedType: SerializedStorageMarketDataRef; + serializedName: "Data"; + }; + wallet: { + type: Address | null; + serializedType: SerializedAddress | null; + serializedName: "Wallet"; + }; + miner: { + type: string; // using string until we can support more address types in Address + serializedType: string; + serializedName: "Miner"; + }; + epochPrice: { + type: bigint; + serializedType: string; + serializedName: "EpochPrice"; + }; + minBlocksDuration: { + type: number; + serializedType: number; + serializedName: "MinBlocksDuration"; + }; + providerCollateral: { + type: bigint; + serializedType: string; + serializedName: "ProviderCollateral"; + }; + dealStartEpoch: { + type: number; + serializedType: number; + serializedName: "dealStartEpoch"; + }; + fastRetrieval: { + type: boolean; + serializedType: boolean; + serializedName: "FastRetrieval"; + }; + verifiedDeal: { + type: boolean; + serializedType: boolean; + serializedName: "VerifiedDeal"; + }; + }; +}; + +class StartDealParams + extends SerializableObject + implements DeserializedObject { + get config(): Definitions { + return { + data: { + deserializedName: "data", + serializedName: "Data", + defaultValue: options => new StorageMarketDataRef(options) + }, + wallet: { + deserializedName: "wallet", + serializedName: "Wallet", + defaultValue: options => (options ? new Address(options) : null) + }, + miner: { + deserializedName: "miner", + serializedName: "Miner", + defaultValue: "t01000" + }, + epochPrice: { + deserializedName: "epochPrice", + serializedName: "EpochPrice", + defaultValue: literal => (literal ? BigInt(literal) : 2500n) + }, + minBlocksDuration: { + deserializedName: "minBlocksDuration", + serializedName: "MinBlocksDuration", + defaultValue: 300 + }, + providerCollateral: { + deserializedName: "providerCollateral", + serializedName: "ProviderCollateral", + defaultValue: literal => (literal ? BigInt(literal) : 0n) + }, + dealStartEpoch: { + deserializedName: "dealStartEpoch", + serializedName: "dealStartEpoch", + defaultValue: 0 + }, + fastRetrieval: { + deserializedName: "fastRetrieval", + serializedName: "FastRetrieval", + defaultValue: false + }, + verifiedDeal: { + deserializedName: "verifiedDeal", + serializedName: "VerifiedDeal", + defaultValue: false + } + }; + } + + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.data = super.initializeValue(this.config.data, options); + this.wallet = super.initializeValue(this.config.wallet, options); + this.miner = super.initializeValue(this.config.miner, options); + this.epochPrice = super.initializeValue(this.config.epochPrice, options); + this.minBlocksDuration = super.initializeValue( + this.config.minBlocksDuration, + options + ); + this.providerCollateral = super.initializeValue( + this.config.providerCollateral, + options + ); + this.dealStartEpoch = super.initializeValue( + this.config.dealStartEpoch, + options + ); + this.fastRetrieval = super.initializeValue( + this.config.fastRetrieval, + options + ); + this.verifiedDeal = super.initializeValue( + this.config.verifiedDeal, + options + ); + } + + data: StorageMarketDataRef; + wallet: Address | null; + miner: string; + epochPrice: bigint; + minBlocksDuration: number; + providerCollateral: bigint; + dealStartEpoch: number; + fastRetrieval: boolean; + verifiedDeal: boolean; +} + +type SerializedStartDealParams = SerializedObject; + +export { StartDealParams, SerializedStartDealParams }; diff --git a/src/chains/filecoin/filecoin/src/things/storage-market-data-ref.ts b/src/chains/filecoin/filecoin/src/things/storage-market-data-ref.ts new file mode 100644 index 0000000000..00d176d269 --- /dev/null +++ b/src/chains/filecoin/filecoin/src/things/storage-market-data-ref.ts @@ -0,0 +1,88 @@ +import { RootCID, SerializedRootCID } from "./root-cid"; +import { + SerializableObject, + SerializedObject, + DeserializedObject, + Definitions +} from "./serializable-object"; + +// https://pkg.go.dev/github.com/filecoin-project/go-fil-markets@v1.1.1/storagemarket#DataRef + +type StorageMarketDataRefConfig = { + properties: { + transferType: { + type: "graphsync"; + serializedType: "graphsync"; + serializedName: "TransferType"; + }; + root: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "Root"; + }; + pieceCid: { + type: RootCID | null; + serializedType: SerializedRootCID | null; + serializedName: "PieceCid"; + }; + pieceSize: { + type: 0; + serializedType: 0; + serializedName: "PieceSize"; + }; + }; +}; + +class StorageMarketDataRef + extends SerializableObject + implements DeserializedObject { + get config(): Definitions { + return { + transferType: { + deserializedName: "transferType", + serializedName: "TransferType", + defaultValue: "graphsync" + }, + root: { + deserializedName: "root", + serializedName: "Root", + defaultValue: options => new RootCID(options) + }, + pieceCid: { + deserializedName: "pieceCid", + serializedName: "PieceCid", + defaultValue: null + }, + pieceSize: { + deserializedName: "pieceSize", + serializedName: "PieceSize", + defaultValue: 0 + } + }; + } + + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.transferType = super.initializeValue( + this.config.transferType, + options + ); + this.root = super.initializeValue(this.config.root, options); + this.pieceCid = super.initializeValue(this.config.pieceCid, options); + this.pieceSize = super.initializeValue(this.config.pieceSize, options); + } + + transferType: "graphsync"; + root: RootCID; + pieceCid: RootCID | null; + pieceSize: 0; +} + +type SerializedStorageMarketDataRef = SerializedObject; + +export { StorageMarketDataRef, SerializedStorageMarketDataRef }; diff --git a/src/chains/filecoin/filecoin/src/things/ticket.ts b/src/chains/filecoin/filecoin/src/things/ticket.ts index 9a5ca947a6..5ec3db1dfe 100644 --- a/src/chains/filecoin/filecoin/src/things/ticket.ts +++ b/src/chains/filecoin/filecoin/src/things/ticket.ts @@ -5,10 +5,12 @@ import { Definitions } from "./serializable-object"; +// https://pkg.go.dev/github.com/filecoin-project/lotus@v1.4.0/chain/types#Ticket + interface TicketConfig { properties: { vrfProof: { - type: string; + type: Buffer; serializedType: string; serializedName: "VRFProof"; }; @@ -21,14 +23,27 @@ class Ticket get config(): Definitions { return { vrfProof: { + deserializedName: "vrfProof", serializedName: "VRFProof", - defaultValue: - "tPnuOjWp9LS/w5VuB+ALc0wn+0aNRF9SkOSykAszkppjnSYGY1qFhhI2fI7PvS39FufkkH8AKCqctU23D4EkAKqZvnMEp8eVjy528BPWE394/n2Z4pJCgjHau2bK26vN" + defaultValue: literal => + typeof literal !== "undefined" + ? Buffer.from(literal, "base64") + : Buffer.from([0]) } }; } - vrfProof: string; + constructor( + options?: + | Partial> + | Partial> + ) { + super(); + + this.vrfProof = super.initializeValue(this.config.vrfProof, options); + } + + vrfProof: Buffer; } type SerializedTicket = SerializedObject; diff --git a/src/chains/filecoin/filecoin/src/things/tipset.ts b/src/chains/filecoin/filecoin/src/things/tipset.ts index b50d5bf60b..3f2978b403 100644 --- a/src/chains/filecoin/filecoin/src/things/tipset.ts +++ b/src/chains/filecoin/filecoin/src/things/tipset.ts @@ -1,4 +1,4 @@ -import { Block, SerializedBlock } from "./block"; +import { BlockHeader, SerializedBlockHeader } from "./block-header"; import { SerializableObject, DeserializedObject, @@ -7,6 +7,8 @@ import { } from "./serializable-object"; import { RootCID, SerializedRootCID } from "./root-cid"; +// https://pkg.go.dev/github.com/filecoin-project/lotus@v1.4.0/chain/types#TipSet + interface TipsetConfig { properties: { cids: { @@ -15,8 +17,8 @@ interface TipsetConfig { serializedName: "Cids"; }; blocks: { - type: Array; - serializedType: Array; + type: Array; + serializedType: Array; serializedName: "Blocks"; }; height: { @@ -33,15 +35,19 @@ class Tipset get config(): Definitions { return { cids: { + deserializedName: "cids", serializedName: "Cids", - defaultValue: (options = []) => - options.map(rootCid => new RootCID(rootCid)) + defaultValue: options => + options ? options.map(rootCid => new RootCID(rootCid)) : [] }, blocks: { + deserializedName: "blocks", serializedName: "Blocks", - defaultValue: (options = []) => options.map(block => new Block(block)) + defaultValue: options => + options ? options.map(block => new BlockHeader(block)) : [] }, height: { + deserializedName: "height", serializedName: "Height", defaultValue: 0 } @@ -53,22 +59,32 @@ class Tipset | Partial> | Partial> ) { - super(options); + super(); + + this.cids = super.initializeValue(this.config.cids, options); + this.blocks = super.initializeValue(this.config.blocks, options); + this.height = super.initializeValue(this.config.height, options); // Calculate Cid's if not specified - if (this.cids.length == 0) { + if (this.cids.length === 0) { for (const block of this.blocks) { this.cids.push( new RootCID({ - "/": block.cid + root: block.cid }) ); } } } + /** + * An array that contains the BlockHeader.cid(). + * If not provided, constructor will auto add this array. + * There's no documentation specifying this, so here is + * the reference Implementation: https://git.io/Jt3VM + */ cids: Array; - blocks: Array; + blocks: Array; height: number; } diff --git a/src/chains/filecoin/filecoin/src/types/storage-deal-status.ts b/src/chains/filecoin/filecoin/src/types/storage-deal-status.ts new file mode 100644 index 0000000000..f39b95cd62 --- /dev/null +++ b/src/chains/filecoin/filecoin/src/types/storage-deal-status.ts @@ -0,0 +1,145 @@ +// Note that the runtime number values of these enums match +// *exactly* the number values of the same states found at +// go-fil-markets/storagemarket/dealstatus.go (https://git.io/JtJAS) +// **DO NOT TRUST** other sources (i.e. lotus/lotuspond/front/src/Client.js +// or js-lotus-client-workshop/src/08-deals/deal-list.js) +// Don't reorganize unless you know what you're doing. + +// Updated to v1.1.1 (https://github.com/filecoin-project/go-fil-markets/blob/v1.1.1/storagemarket/dealstatus.go) +export enum StorageDealStatus { + // Unknown means the current status of a deal is undefined + Unknown, + + // ProposalNotFound is a status returned in responses when the deal itself cannot + // be located + ProposalNotFound, + + // ProposalRejected is returned by a StorageProvider when it chooses not to accept + // a DealProposal + ProposalRejected, + + // ProposalAccepted indicates an intent to accept a storage deal proposal + ProposalAccepted, + + // Staged means a deal has been published and data is ready to be put into a sector + Staged, + + // Sealing means a deal is in a sector that is being sealed + Sealing, + + // Finalizing means a deal is in a sealed sector and we're doing final + // housekeeping before marking it active + Finalizing, + + // Active means a deal is in a sealed sector and the miner is proving the data + // for the deal + Active, + + // Expired means a deal has passed its final epoch and is expired + Expired, + + // Slashed means the deal was in a sector that got slashed from failing to prove + Slashed, + + // Rejecting means the Provider has rejected the deal, and will send a rejection response + Rejecting, + + // Failing means something has gone wrong in a deal. Once data is cleaned up the deal will finalize on + // Error + Failing, + + // FundsReserved means we've deposited funds as necessary to create a deal, ready to move forward + FundsReserved, + + // CheckForAcceptance means the client is waiting for a provider to seal and publish a deal + CheckForAcceptance, + + // Validating means the provider is validating that deal parameters are good for a proposal + Validating, + + // AcceptWait means the provider is running any custom decision logic to decide whether or not to accept the deal + AcceptWait, + + // StartDataTransfer means data transfer is beginning + StartDataTransfer, + + // Transferring means data is being sent from the client to the provider via the data transfer module + Transferring, + + // WaitingForData indicates either a manual transfer + // or that the provider has not received a data transfer request from the client + WaitingForData, + + // VerifyData means data has been transferred and we are attempting to verify it against the PieceCID + VerifyData, + + // ReserveProviderFunds means that provider is making sure it has adequate funds for the deal in the StorageMarketActor + ReserveProviderFunds, + + // ReserveClientFunds means that client is making sure it has adequate funds for the deal in the StorageMarketActor + ReserveClientFunds, + + // ProviderFunding means that the provider has deposited funds in the StorageMarketActor and it is waiting + // to see the funds appear in its balance + ProviderFunding, + + // ClientFunding means that the client has deposited funds in the StorageMarketActor and it is waiting + // to see the funds appear in its balance + ClientFunding, + + // Publish means the deal is ready to be published on chain + Publish, + + // Publishing means the deal has been published but we are waiting for it to appear on chain + Publishing, + + // Error means the deal has failed due to an error, and no further updates will occur + Error, + + // ProviderTransferAwaitRestart means the provider has restarted while data + // was being transferred from client to provider, and will wait for the client to + // resume the transfer + ProviderTransferAwaitRestart, + + // ClientTransferRestart means a storage deal data transfer from client to provider will be restarted + // by the client + ClientTransferRestart, + + // AwaitingPreCommit means a deal is ready and must be pre-committed + AwaitingPreCommit +} + +export let terminalStates: Array = [ + StorageDealStatus.ProposalNotFound, + StorageDealStatus.ProposalRejected, + StorageDealStatus.Error, + StorageDealStatus.Expired +]; + +export let nextSuccessfulState: Record = [ + StorageDealStatus.Validating, + StorageDealStatus.Staged, + StorageDealStatus.ReserveProviderFunds, + StorageDealStatus.ReserveClientFunds, + StorageDealStatus.FundsReserved, + StorageDealStatus.ProviderFunding, + StorageDealStatus.ClientFunding, + StorageDealStatus.Publish, + StorageDealStatus.Publishing, + StorageDealStatus.Transferring, + StorageDealStatus.Sealing, + StorageDealStatus.Active +].reduce((obj, currentValue, index, array) => { + // This creates an object linking each state to its next state + + let nextValue: StorageDealStatus; + if (index + 1 < array.length) { + nextValue = array[index + 1]; + } else { + nextValue = array[index]; + } + + obj[currentValue] = nextValue; + + return obj; +}, {} as Record); diff --git a/src/chains/filecoin/filecoin/src/types/subscriptions.ts b/src/chains/filecoin/filecoin/src/types/subscriptions.ts new file mode 100644 index 0000000000..b9ddb312bf --- /dev/null +++ b/src/chains/filecoin/filecoin/src/types/subscriptions.ts @@ -0,0 +1,7 @@ +export enum SubscriptionMethod { + ChannelUpdated = "xrpc.ch.val", + ChannelClosed = "xrpc.ch.close", + SubscriptionCanceled = "xrpc.cancel" +} + +export type SubscriptionId = string; diff --git a/src/chains/filecoin/filecoin/tests/api/filecoin/filecoin.test.ts b/src/chains/filecoin/filecoin/tests/api/filecoin/deals.test.ts similarity index 56% rename from src/chains/filecoin/filecoin/tests/api/filecoin/filecoin.test.ts rename to src/chains/filecoin/filecoin/tests/api/filecoin/deals.test.ts index cdcc0813e4..7c04017d8d 100644 --- a/src/chains/filecoin/filecoin/tests/api/filecoin/filecoin.test.ts +++ b/src/chains/filecoin/filecoin/tests/api/filecoin/deals.test.ts @@ -4,13 +4,13 @@ import getProvider from "../../helpers/getProvider"; import getIpfsClient from "../../helpers/getIpfsClient"; import { IPFSClient } from "ipfs-http-client"; import { CID } from "../../../src/things/cid"; -import { Address } from "../../../src/things/address"; -import { StorageProposal } from "../../../src/things/storage-proposal"; +import { StartDealParams } from "../../../src/things/start-deal-params"; import { RootCID } from "../../../src/things/root-cid"; -import { StorageProposalData } from "../../../src/things/storage-proposal-data"; -import { SerializedDeal } from "../../../src/things/deal"; -import { SerializedRetrievalOffer } from "../../../src/things/retrieval-offer"; +import { StorageMarketDataRef } from "../../../src/things/storage-market-data-ref"; +import { SerializedDealInfo } from "../../../src/things/deal-info"; +import { SerializedRetrievalOrder } from "../../../src/things/retrieval-order"; import BN from "bn.js"; +import { SerializedQueryOffer } from "../../../src/things/query-offer"; const LotusRPC = require("@filecoin-shipyard/lotus-client-rpc").LotusRPC; @@ -27,73 +27,9 @@ describe("api", () => { }); after(async () => { - await provider.stop(); - }); - - describe("General request processing", () => { - it("should return a value over JSON RPC", async () => { - // Note the Filecoin Provider strips the JSON RPC details - // from the response. - const genesis = await provider.send({ - jsonrpc: "2.0", - id: "0", - method: "Filecoin.ChainGetGenesis" - }); - - assert(CID.isValid(genesis["Cids"][0]["/"])); - }); - - // TODO: Test for unsupported methods - }); - - describe("Filecoin.ChainGetGenesis", () => { - it("should return a value", async () => { - const genesis = await client.chainGetGenesis(); - assert(CID.isValid(genesis["Cids"][0]["/"])); - }); - }); - - describe("Filecoin.ChainHead", () => { - it("should return a serialized tipset with blocks", async () => { - const head = await client.chainHead(); - assert(head.Blocks.length > 0); - }); - }); - - describe("Filecoin.StateListMiners", () => { - it("should return a single miner", async () => { - const miners = await client.stateListMiners(); - assert.strictEqual(miners.length, 1); - assert.strictEqual(miners[0], "t01000"); - }); - }); - - describe("Filecoin.WalletDefaultAddress", () => { - it("should return a single address", async () => { - const address = await client.walletDefaultAddress(); - assert.strictEqual(address.length, 86); - assert.strictEqual(address.indexOf("t3"), 0); - assert(Address.isValid(address)); - }); - }); - - describe("Filecoin.WalletBalance", () => { - let address: string; - - beforeEach(async () => { - address = await client.walletDefaultAddress(); - }); - - it("should return a balance for the default address", async () => { - const balance = await client.walletBalance(address); - assert.strictEqual(balance, "500000000000000000000000"); - }); - - it("should not return a balance for any other address", async () => { - let otherAddress = Address.random().value; - const balance = await client.walletBalance(otherAddress); - assert.strictEqual(balance, "0"); - }); + if (provider) { + await provider.stop(); + } }); describe("Filecoin.ClientStartDeal and Filecoin.ClientListDeals", () => { @@ -114,18 +50,17 @@ describe("api", () => { let result = await ipfs.add(data); let cid = result.path; - let proposal = new StorageProposal({ - data: new StorageProposalData({ + let proposal = new StartDealParams({ + data: new StorageMarketDataRef({ transferType: "graphsync", root: new RootCID({ "/": cid }), - pieceCid: null, pieceSize: 0 }), - wallet: address, + Wallet: address, miner: miners[0], - epochPrice: "2500", + epochPrice: 2500n, minBlocksDuration: 300 }); @@ -138,7 +73,7 @@ describe("api", () => { assert.strictEqual(deals.length, 1); - let deal: SerializedDeal = deals[0]; + let deal: SerializedDealInfo = deals[0]; assert.strictEqual(deal.ProposalCid["/"], proposalCid["/"]); assert.strictEqual(deal.Size, expectedSize); @@ -150,7 +85,7 @@ describe("api", () => { describe("Filecoin.ClientFindData, Filecoin.ClientRetrieve, and Filecoin.ClientHasLocal", () => { let ipfs: IPFSClient; - let offer: SerializedRetrievalOffer; + let offer: SerializedQueryOffer; let address: string; let beginningBalance: string; @@ -175,6 +110,7 @@ describe("api", () => { offer = offers[0]; assert.ok(offer); + assert.strictEqual(offer.Root["/"], result.path); assert.strictEqual(offer.Size, expectedSize); assert.strictEqual(offer.MinPrice, expectedMinPrice); @@ -184,7 +120,20 @@ describe("api", () => { }); it("should 'retrieve' without error (but we all know it's not actually retrieving anything...), and subtract balance", async () => { - await client.clientRetrieve(offer); + const order: SerializedRetrievalOrder = { + Root: offer.Root, + Piece: offer.Piece, + Size: offer.Size, + Total: offer.MinPrice, + UnsealPrice: offer.UnsealPrice, + PaymentInterval: offer.PaymentInterval, + PaymentIntervalIncrease: offer.PaymentIntervalIncrease, + Client: address, + Miner: offer.Miner, + MinerPeer: offer.MinerPeer + }; + + await client.clientRetrieve(order); // No error? Great, let's make sure it subtracted the retreival cost. @@ -193,27 +142,35 @@ describe("api", () => { }); it("errors if we try to retrieve a file our IPFS server doesn't know about", async () => { - let err: Error; - let cidIMadeUp = "QmY7Yh4UquoXdL9Fo2XbhXkhBvFoLwmQUfa92pxnxjQuPU"; - let madeUpOffer: SerializedRetrievalOffer = { - Err: "", + let madeUpOrder: SerializedRetrievalOrder = { Root: { "/": cidIMadeUp }, + Piece: { + "/": cidIMadeUp + }, Size: 1234, - MinPrice: "2468", + Total: "2468", + UnsealPrice: "2468", PaymentInterval: 1048576, PaymentIntervalIncrease: 1048576, - Miner: "t0100", - MinerPeerID: "6vuxqgevbl6irx7tymbj7o4t8bz1s5vy88zmum7flxywy1qugjfd" + Client: address, + Miner: "t01000", + MinerPeer: { + Address: "t01000", + ID: "t01000", + PieceCID: { + "/": "6vuxqgevbl6irx7tymbj7o4t8bz1s5vy88zmum7flxywy1qugjfd" + } + } }; - let error: Error; + let error: Error | undefined; try { - await client.clientRetrieve(madeUpOffer); + await client.clientRetrieve(madeUpOrder); } catch (e) { error = e; } @@ -223,7 +180,7 @@ describe("api", () => { "undefined", "Expected ClientRetrieve to throw an error!" ); - assert(error.message.indexOf("Object not found") >= 0); + assert(error!.message.indexOf("Object not found") >= 0); let hasLocal = await client.clientHasLocal({ "/": cidIMadeUp }); diff --git a/src/chains/filecoin/filecoin/tests/api/filecoin/generic.test.ts b/src/chains/filecoin/filecoin/tests/api/filecoin/generic.test.ts new file mode 100644 index 0000000000..2d0b39e14d --- /dev/null +++ b/src/chains/filecoin/filecoin/tests/api/filecoin/generic.test.ts @@ -0,0 +1,132 @@ +import assert from "assert"; +import FilecoinProvider from "../../../src/provider"; +import getProvider from "../../helpers/getProvider"; +import { CID } from "../../../src/things/cid"; +import LotusSchema from "@filecoin-shipyard/lotus-client-schema"; +import GanacheSchema from "../../../src/schema"; +import FilecoinApi from "../../../src/api"; + +const LotusRPC = require("@filecoin-shipyard/lotus-client-rpc").LotusRPC; + +type LotusClient = any; + +describe("api", () => { + describe("filecoin", () => { + let provider: FilecoinProvider; + let client: LotusClient; + + before(async () => { + provider = await getProvider(); + client = new LotusRPC(provider, { schema: FilecoinProvider.Schema }); + }); + + after(async () => { + await provider.stop(); + }); + + describe("General request processing", () => { + it("should return a value over JSON RPC", async () => { + // Note the Filecoin Provider strips the JSON RPC details + // from the response. + const genesis = await provider.send({ + jsonrpc: "2.0", + id: "0", + method: "Filecoin.ChainGetGenesis" + }); + + assert(CID.isValid(genesis["Cids"][0]["/"])); + }); + + it("should return invalid methods for all unimplemented methods", async () => { + const combinedMethods = { + ...LotusSchema.mainnet.fullNode.methods, + ...LotusSchema.mainnet.storageMiner.methods, + ...LotusSchema.mainnet.gatewayApi.methods, + ...LotusSchema.mainnet.walletApi.methods, + ...LotusSchema.mainnet.workerApi.methods + }; + const methods = Object.keys(combinedMethods) + .filter( + method => typeof GanacheSchema.methods[method] === "undefined" + ) + .map(method => `Filecoin.${method}`); + + for (const method of methods) { + try { + await provider.send({ + jsonrpc: "2.0", + id: "0", + method: method as any, + params: [] + }); + } catch (e) { + assert.strictEqual( + e.message, + `The method ${method} does not exist/is not available` + ); + continue; + } + + assert.fail(`Unsupported method ${method} was sent successfully`); + } + }); + + it("should only have valid Filecoin.<...> methods", async () => { + const combinedMethods = { + ...LotusSchema.mainnet.fullNode.methods, + ...LotusSchema.mainnet.storageMiner.methods, + ...LotusSchema.mainnet.gatewayApi.methods, + ...LotusSchema.mainnet.walletApi.methods, + ...LotusSchema.mainnet.workerApi.methods + }; + const methods = Object.getOwnPropertyNames(FilecoinApi.prototype) + .filter( + method => method !== "constructor" && method.startsWith("Filecoin.") + ) + .map(method => method.replace("Filecoin.", "")); + + for (const method of methods) { + if (typeof combinedMethods[method] === "undefined") { + assert.fail( + `Filecoin method Filecoin.${method} is implemented, but not part of the official schema` + ); + } + } + }); + }); + + describe("Filecoin.ChainGetGenesis", () => { + it("should return a value", async () => { + const genesis = await client.chainGetGenesis(); + assert(CID.isValid(genesis["Cids"][0]["/"])); + }); + }); + + describe("Filecoin.ChainHead", () => { + it("should return a serialized tipset with blocks", async () => { + const head = await client.chainHead(); + assert.strictEqual(head.Height, 0); + assert(head.Blocks.length > 0); + assert.strictEqual(head.Blocks[0].Height, head.Height); + }); + }); + + describe("Ganache.MineTipset", () => { + it("should return a serialized tipset with blocks", async () => { + const { Height: priorHeight } = await client.chainHead(); + + for (let i = 0; i < 5; i++) { + await provider.send({ + jsonrpc: "2.0", + id: "0", + method: "Ganache.MineTipset" + }); + } + + const { Height: currentHeight } = await client.chainHead(); + + assert.strictEqual(currentHeight, priorHeight + 5); + }); + }); + }); +}); diff --git a/src/chains/filecoin/filecoin/tests/api/filecoin/miners.test.ts b/src/chains/filecoin/filecoin/tests/api/filecoin/miners.test.ts new file mode 100644 index 0000000000..437da36121 --- /dev/null +++ b/src/chains/filecoin/filecoin/tests/api/filecoin/miners.test.ts @@ -0,0 +1,31 @@ +import assert from "assert"; +import FilecoinProvider from "../../../src/provider"; +import getProvider from "../../helpers/getProvider"; + +const LotusRPC = require("@filecoin-shipyard/lotus-client-rpc").LotusRPC; + +type LotusClient = any; + +describe("api", () => { + describe("filecoin", () => { + let provider: FilecoinProvider; + let client: LotusClient; + + before(async () => { + provider = await getProvider(); + client = new LotusRPC(provider, { schema: FilecoinProvider.Schema }); + }); + + after(async () => { + await provider.stop(); + }); + + describe("Filecoin.StateListMiners", () => { + it("should return a single miner", async () => { + const miners = await client.stateListMiners(); + assert.strictEqual(miners.length, 1); + assert.strictEqual(miners[0], "t01000"); + }); + }); + }); +}); diff --git a/src/chains/filecoin/filecoin/tests/api/filecoin/subscriptions-ws.test.ts b/src/chains/filecoin/filecoin/tests/api/filecoin/subscriptions-ws.test.ts new file mode 100644 index 0000000000..e93d1fa163 --- /dev/null +++ b/src/chains/filecoin/filecoin/tests/api/filecoin/subscriptions-ws.test.ts @@ -0,0 +1,166 @@ +import assert from "assert"; +import { SubscriptionMethod } from "../../../src/types/subscriptions"; +import getServer from "../../helpers/getServer"; +import WebSocket from "ws"; +import Server from "../../../../../../packages/core/src/server"; + +describe("api", () => { + describe("filecoin", () => { + let server: Server; + let ws: WebSocket; + const port = 7778; // Use a different port than the default, to test it works + + before(async () => { + server = await getServer(port); + ws = new WebSocket(`ws://localhost:${port}/rpc/v0`); + + await new Promise((resolve, reject) => { + const id = setTimeout(async () => { + await server.close(); + ws.terminate(); + reject("Could not connect to the websocket server"); + }, 2000); + + ws.on("open", () => { + clearTimeout(id); + resolve(); + }); + }); + }); + + after(async () => { + if (ws) { + ws.close(); + } + if (server) { + await server.close(); + } + }); + + it("should subscribe and unsubscribe properly with websockets", async () => { + let numTipsetsReceived = 0; + const chainNotifyId = "1337"; // using something non-zero to ensure functionality + let receivedMessage = false; + let receivedSubscriptionCanceled = false; + let channelId: any; + + ws.on("message", message => { + const response = JSON.parse(message.toString()); + + if (response.result) { + switch (response.id) { + case chainNotifyId: + channelId = response.result; + receivedMessage = true; + break; + case "0": + receivedMessage = true; + assert.strictEqual(response.result.Height, 1); + break; + case "1": + receivedMessage = true; + assert.strictEqual(response.result.Height, 2); + break; + case "2": + receivedMessage = true; + assert.strictEqual(response.result.Height, 3); + break; + case "3": + receivedMessage = true; + assert.strictEqual(response.result.Height, 4); + break; + case "4": + receivedMessage = true; + assert.strictEqual(response.result.Height, 5); + break; + case "5": + receivedMessage = true; + assert.strictEqual(response.result, true); + break; + } + } else if (response.method) { + if (response.method === SubscriptionMethod.SubscriptionCanceled) { + assert.strictEqual(response.params[0], chainNotifyId); + receivedSubscriptionCanceled = true; + } else if (response.method === SubscriptionMethod.ChannelUpdated) { + assert.strictEqual(response.params[0], channelId); + assert.strictEqual(response.params[1].length, 1); // should only have one tipset per headchange + assert.strictEqual( + response.params[1][0].Val.Height, + ++numTipsetsReceived + ); + } + } + }); + + ws.send( + JSON.stringify({ + jsonrpc: "2.0", + id: chainNotifyId, + method: "Filecoin.ChainNotify", + params: [] + }) + ); + + for (let i = 0; i < 100; i++) { + if (receivedMessage) { + break; + } + + await new Promise(resolve => setTimeout(resolve, 50)); + } + + if (!receivedMessage) { + assert.fail("Did not receive response for ChainNotify"); + } + + for (let i = 0; i < 5; i++) { + receivedMessage = false; + ws.send( + JSON.stringify({ + jsonrpc: "2.0", + id: `${i}`, + method: "Ganache.MineTipset", + params: [] + }) + ); + + for (let j = 0; j < 100; j++) { + if (receivedMessage) { + break; + } + + await new Promise(resolve => setTimeout(resolve, 50)); + } + + if (!receivedMessage) { + assert.fail(`Did not receive response for Ganache.MineTipset #${i}`); + } + } + + receivedMessage = false; + ws.send( + JSON.stringify({ + jsonrpc: "2.0", + id: "5", + method: SubscriptionMethod.ChannelClosed, + params: [channelId] + }) + ); + + for (let i = 0; i < 100; i++) { + if (receivedSubscriptionCanceled) { + break; + } + + await new Promise(resolve => setTimeout(resolve, 50)); + } + + if (!receivedSubscriptionCanceled) { + assert.fail( + `Did not receive ${SubscriptionMethod.SubscriptionCanceled} after closing channel/subscription` + ); + } + }); + }).timeout(10000); +}); diff --git a/src/chains/filecoin/filecoin/tests/api/filecoin/subscriptions.test.ts b/src/chains/filecoin/filecoin/tests/api/filecoin/subscriptions.test.ts new file mode 100644 index 0000000000..96b7d80df2 --- /dev/null +++ b/src/chains/filecoin/filecoin/tests/api/filecoin/subscriptions.test.ts @@ -0,0 +1,97 @@ +import assert from "assert"; +import Emittery from "emittery"; +import FilecoinProvider from "../../../src/provider"; +import { SubscriptionMethod } from "../../../src/types/subscriptions"; +import getProvider from "../../helpers/getProvider"; + +const LotusRPC = require("@filecoin-shipyard/lotus-client-rpc").LotusRPC; + +type LotusClient = any; + +describe("api", () => { + describe("filecoin", () => { + let provider: FilecoinProvider; + let client: LotusClient; + + before(async () => { + provider = await getProvider(); + client = new LotusRPC(provider, { schema: FilecoinProvider.Schema }); + }); + + after(async () => { + await provider.stop(); + }); + + describe("Filecoin.ChainNotify", () => { + let unsubscribe: Emittery.UnsubscribeFn; + let numTipsetsReceived = 0; + + it("should receive updates for new tipsets", async () => { + const subscription = await client.chainNotify((_changes: any) => { + numTipsetsReceived++; + }); + + unsubscribe = subscription[0]; + + for (let i = 0; i < 5; i++) { + await provider.send({ + jsonrpc: "2.0", + id: "0", + method: "Ganache.MineTipset" + }); + } + + assert.strictEqual(numTipsetsReceived, 5); + }); + + it("should cancel subscription via unsubscribe function", async () => { + unsubscribe(); + + await provider.send({ + jsonrpc: "2.0", + id: "0", + method: "Ganache.MineTipset" + }); + + assert.strictEqual(numTipsetsReceived, 5); + }); + + it("should cancel subscription via RPC method", async () => { + numTipsetsReceived = 0; + + const subscription = await client.chainNotify((_changes: any) => { + numTipsetsReceived++; + }); + + const subscriptionId: string = await subscription[1]; + + for (let i = 0; i < 5; i++) { + await provider.send({ + jsonrpc: "2.0", + id: `${i}`, + method: "Ganache.MineTipset" + }); + } + + assert.strictEqual(numTipsetsReceived, 5); + + const success = await provider.send({ + jsonrpc: "2.0", + id: "6", + method: SubscriptionMethod.ChannelClosed, + params: [subscriptionId] + }); + + assert.strictEqual(success, true); + + await provider.send({ + jsonrpc: "2.0", + id: `7`, + method: "Ganache.MineTipset" + }); + + assert.strictEqual(numTipsetsReceived, 5); + }); + }); + }); +}); diff --git a/src/chains/filecoin/filecoin/tests/api/filecoin/wallet.test.ts b/src/chains/filecoin/filecoin/tests/api/filecoin/wallet.test.ts new file mode 100644 index 0000000000..01383b3032 --- /dev/null +++ b/src/chains/filecoin/filecoin/tests/api/filecoin/wallet.test.ts @@ -0,0 +1,52 @@ +import assert from "assert"; +import FilecoinProvider from "../../../src/provider"; +import getProvider from "../../helpers/getProvider"; +import { Address } from "../../../src/things/address"; + +const LotusRPC = require("@filecoin-shipyard/lotus-client-rpc").LotusRPC; + +type LotusClient = any; + +describe("api", () => { + describe("filecoin", () => { + let provider: FilecoinProvider; + let client: LotusClient; + + before(async () => { + provider = await getProvider(); + client = new LotusRPC(provider, { schema: FilecoinProvider.Schema }); + }); + + after(async () => { + await provider.stop(); + }); + + describe("Filecoin.WalletDefaultAddress", () => { + it("should return a single address", async () => { + const address = await client.walletDefaultAddress(); + assert.strictEqual(address.length, 86); + assert.strictEqual(address.indexOf("t3"), 0); + assert(Address.isValid(address)); + }); + }); + + describe("Filecoin.WalletBalance", () => { + let address: string; + + beforeEach(async () => { + address = await client.walletDefaultAddress(); + }); + + it("should return a balance for the default address", async () => { + const balance = await client.walletBalance(address); + assert.strictEqual(balance, "500000000000000000000"); + }); + + it("should not return a balance for any other address", async () => { + let otherAddress = Address.random().value; + const balance = await client.walletBalance(otherAddress); + assert.strictEqual(balance, "0"); + }); + }); + }); +}); diff --git a/src/chains/filecoin/filecoin/tests/blockchain/blockchain.test.ts b/src/chains/filecoin/filecoin/tests/blockchain/blockchain.test.ts index ea252ea8f0..1722ef21cd 100644 --- a/src/chains/filecoin/filecoin/tests/blockchain/blockchain.test.ts +++ b/src/chains/filecoin/filecoin/tests/blockchain/blockchain.test.ts @@ -2,10 +2,10 @@ import assert from "assert"; import Blockchain from "../../src/blockchain"; import { Tipset } from "../../src/things/tipset"; import IpfsHttpClient from "ipfs-http-client"; -import { StorageProposal } from "../../src/things/storage-proposal"; -import { StorageProposalData } from "../../src/things/storage-proposal-data"; +import { StartDealParams } from "../../src/things/start-deal-params"; +import { StorageMarketDataRef } from "../../src/things/storage-market-data-ref"; import { RootCID } from "../../src/things/root-cid"; -import { DealState } from "../../src/deal-state"; +import { StorageDealStatus } from "../../src/types/storage-deal-status"; import { FilecoinOptionsConfig } from "@ganache/filecoin-options"; @@ -14,7 +14,15 @@ describe("Blockchain", () => { let blockchain: Blockchain; before(async () => { - blockchain = new Blockchain(FilecoinOptionsConfig.normalize({})); + blockchain = new Blockchain( + FilecoinOptionsConfig.normalize({ + logging: { + logger: { + log: () => {} + } + } + }) + ); await blockchain.waitForReady(); }); @@ -50,7 +58,12 @@ describe("Blockchain", () => { let blockchain = new Blockchain( FilecoinOptionsConfig.normalize({ miner: { - blockTime: 100 + blockTime: 0.1 + }, + logging: { + logger: { + log: () => {} + } } }) ); @@ -58,13 +71,16 @@ describe("Blockchain", () => { try { await blockchain.waitForReady(); - // After 1 second, we should have well over 4 blocks - // I'm not checking for exactly 5 to dodge race conditions - await new Promise(resolve => setTimeout(resolve, 1000)); + // After 0.5 seconds, we should have at least 3 blocks and no more than 10 blocks + // Github CI is so unpredictable with their burstable cpus + await new Promise(resolve => setTimeout(resolve, 500)); let latest: Tipset = blockchain.latestTipset(); - assert(latest.height >= 4); + assert( + latest.height >= 3 || latest.height <= 10, + `Expected between 3 and 10 blocks to be mined, but got ${latest.height}` + ); } finally { blockchain.stop(); } @@ -73,7 +89,15 @@ describe("Blockchain", () => { describe("ipfs server", () => { it("creates an ipfs server", async () => { - let blockchain = new Blockchain(FilecoinOptionsConfig.normalize({})); + let blockchain = new Blockchain( + FilecoinOptionsConfig.normalize({ + logging: { + logger: { + log: () => {} + } + } + }) + ); try { await blockchain.waitForReady(); @@ -111,35 +135,41 @@ describe("Blockchain", () => { it("advances state of in process deals on every block", async () => { blockchain = new Blockchain( FilecoinOptionsConfig.normalize({ - miner: { automining: false } + miner: { + blockTime: -1 + }, + logging: { + logger: { + log: () => {} + } + } }) ); await blockchain.waitForReady(); - let result = await blockchain.ipfs.add("some data"); + let result = await blockchain.ipfs!.add("some data"); - let proposal = new StorageProposal({ - data: new StorageProposalData({ + let proposal = new StartDealParams({ + data: new StorageMarketDataRef({ transferType: "graphsync", root: new RootCID({ "/": result.path }), - pieceCid: null, pieceSize: 0 }), wallet: blockchain.address, miner: blockchain.miner, - epochPrice: "2500", + epochPrice: 2500n, minBlocksDuration: 300 }); - let { "/": proposalCid } = await blockchain.startDeal(proposal); + let { root: proposalCid } = await blockchain.startDeal(proposal); // First state should be validating assert.strictEqual( blockchain.dealsByCid[proposalCid.value].state, - DealState.Validating + StorageDealStatus.Validating ); await blockchain.mineTipset(); @@ -147,22 +177,23 @@ describe("Blockchain", () => { // Next state should be Staged assert.strictEqual( blockchain.dealsByCid[proposalCid.value].state, - DealState.Staged + StorageDealStatus.Staged ); await blockchain.mineTipset(); - // Next state should be EnsureProviderFunds + // Next state should be ReserveProviderFunds assert.strictEqual( blockchain.dealsByCid[proposalCid.value].state, - DealState.EnsureProviderFunds + StorageDealStatus.ReserveProviderFunds ); // ... and on and on // Let's mine all the way to the Sealing state while ( - blockchain.dealsByCid[proposalCid.value].state != DealState.Sealing + blockchain.dealsByCid[proposalCid.value].state != + StorageDealStatus.Sealing ) { await blockchain.mineTipset(); } @@ -170,7 +201,7 @@ describe("Blockchain", () => { // The deal should still be considered in process, since it's still sealing assert.strictEqual(blockchain.inProcessDeals.length, 1); assert.strictEqual( - blockchain.inProcessDeals[0].proposalCid["/"].value, + blockchain.inProcessDeals[0].proposalCid.root.value, proposalCid.value ); @@ -180,7 +211,7 @@ describe("Blockchain", () => { assert.strictEqual( blockchain.dealsByCid[proposalCid.value].state, - DealState.Active + StorageDealStatus.Active ); assert.strictEqual(blockchain.inProcessDeals.length, 0); }); @@ -189,37 +220,41 @@ describe("Blockchain", () => { blockchain = new Blockchain( FilecoinOptionsConfig.normalize({ miner: { - automining: true + blockTime: 0 + }, + logging: { + logger: { + log: () => {} + } } }) ); await blockchain.waitForReady(); - let result = await blockchain.ipfs.add("some data"); + let result = await blockchain.ipfs!.add("some data"); - let proposal = new StorageProposal({ - data: new StorageProposalData({ + let proposal = new StartDealParams({ + data: new StorageMarketDataRef({ transferType: "graphsync", root: new RootCID({ "/": result.path }), - pieceCid: null, pieceSize: 0 }), wallet: blockchain.address, miner: blockchain.miner, - epochPrice: "2500", + epochPrice: 2500n, minBlocksDuration: 300 }); - let { "/": proposalCid } = await blockchain.startDeal(proposal); + let { root: proposalCid } = await blockchain.startDeal(proposal); // Since we're automining, starting the deal will trigger // the state to be state to be set to active. assert.strictEqual( blockchain.dealsByCid[proposalCid.value].state, - DealState.Active + StorageDealStatus.Active ); // We create 1 tipset per state change. Let's make sure that occurred. @@ -230,26 +265,49 @@ describe("Blockchain", () => { describe("determinism", () => { let blockchain: Blockchain; - before(async () => { + const expectedAddress = + "t3qdqduswwvsvq72iwppn2vytvq2mt7qi5nensswvawpdkmudnzxooi45edyflgnohrfvijy77pn66247nttzq"; + + afterEach(async () => { + if (blockchain) { + await blockchain.stop(); + } + }); + + it("creates the expected address from seed", async () => { blockchain = new Blockchain( FilecoinOptionsConfig.normalize({ wallet: { seed: "tim is a swell guy" + }, + logging: { + logger: { + log: () => {} + } } }) ); await blockchain.waitForReady(); - }); - after(async () => { - await blockchain.stop(); + assert.strictEqual(blockchain.address.value, expectedAddress); }); - it("creates the expected address from seed", async () => { - let expectedAddress = - "t3teloaxbdlmh3q3pbnwofxmpg4oszq6p6ohbj2b5ya6evk3gqi4qfdksjq2vanvsppp634uadfuka7igxymca"; + it("uses the seed to create a different level of determinism", async () => { + blockchain = new Blockchain( + FilecoinOptionsConfig.normalize({ + wallet: { + seed: "tim is a swell person" + }, + logging: { + logger: { + log: () => {} + } + } + }) + ); + await blockchain.waitForReady(); - assert.strictEqual(blockchain.address.value, expectedAddress); + assert.notStrictEqual(blockchain.address.value, expectedAddress); }); }); }); diff --git a/src/chains/filecoin/filecoin/tests/helpers/getProvider.ts b/src/chains/filecoin/filecoin/tests/helpers/getProvider.ts index 45c2537f8b..d34d054a25 100644 --- a/src/chains/filecoin/filecoin/tests/helpers/getProvider.ts +++ b/src/chains/filecoin/filecoin/tests/helpers/getProvider.ts @@ -8,6 +8,11 @@ const getProvider = async () => { { chain: { ipfsPort: 5002 // Use a different port than the default, to test it works + }, + logging: { + logger: { + log: () => {} + } } }, executor diff --git a/src/chains/filecoin/filecoin/tests/helpers/getServer.ts b/src/chains/filecoin/filecoin/tests/helpers/getServer.ts new file mode 100644 index 0000000000..b5cf76401e --- /dev/null +++ b/src/chains/filecoin/filecoin/tests/helpers/getServer.ts @@ -0,0 +1,23 @@ +import { FilecoinFlavorName } from "../../../../../packages/flavors"; +import Server from "../../../../../packages/core/src/server"; + +const getServer = async (port: number) => { + const server = new Server({ + flavor: FilecoinFlavorName, + server: { + ws: true + }, + chain: { + ipfsPort: 5002 // Use a different port than the default, to test it works + }, + logging: { + logger: { + log: () => {} + } + } + }); + await new Promise(resolve => server.listen(port, resolve)); + return server; +}; + +export default getServer; diff --git a/src/chains/filecoin/filecoin/tests/things/things.test.ts b/src/chains/filecoin/filecoin/tests/things/things.test.ts index 75a3207142..31914afd28 100644 --- a/src/chains/filecoin/filecoin/tests/things/things.test.ts +++ b/src/chains/filecoin/filecoin/tests/things/things.test.ts @@ -2,8 +2,10 @@ import assert from "assert"; import { RootCID } from "../../src/things/root-cid"; import { CID } from "../../src/things/cid"; import { Tipset } from "../../src/things/tipset"; -import { Block } from "../../src/things/block"; +import { BlockHeader } from "../../src/things/block-header"; import { Address } from "../../src/things/address"; +import IPFSCid from "cids"; +import multihashing from "multihashing"; describe("things", () => { describe("general", () => { @@ -18,14 +20,14 @@ describe("things", () => { // Note that the CID is defined as an object, which makes the type of data // passed into the constructor a deserialized object let rootCidFromDeserializedData = new RootCID({ - "/": new CID( + root: new CID( "badvu4qhg4y390tu5i4ongi9t2vdf429cl7kp7tcsnbas1f5d66zeb4q30mbsl" ) }); assert.strictEqual( - rootCidFromSerializedData["/"].value, - rootCidFromDeserializedData["/"].value + rootCidFromSerializedData.root.value, + rootCidFromDeserializedData.root.value ); // Now let's try a more complex one that has different keys and includes arrays @@ -43,7 +45,7 @@ describe("things", () => { let tipsetFromDeserializedData = new Tipset({ cids: [ new RootCID({ - "/": new CID( + root: new CID( "badvu4qhg4y390tu5i4ongi9t2vdf429cl7kp7tcsnbas1f5d66zeb4q30mbsl" ) }) @@ -53,8 +55,8 @@ describe("things", () => { }); assert.strictEqual( - tipsetFromSerializedData.cids[0]["/"].value, - tipsetFromDeserializedData.cids[0]["/"].value + tipsetFromSerializedData.cids[0].root.value, + tipsetFromDeserializedData.cids[0].root.value ); }); }); @@ -70,7 +72,7 @@ describe("things", () => { }); it("will error if no value is passed into the constructor", async () => { - let error: Error; + let error: Error | undefined; try { new CID(); @@ -83,42 +85,32 @@ describe("things", () => { "undefined", "Expected CID constructor to throw an error on empty value!" ); - assert.strictEqual(error.message, "A value is required for class CID"); + assert.strictEqual(error!.message, "A value is required for class CID"); }); }); describe("Block", () => { it("has default values", async () => { - let timestamp = new Date().getTime(); + let timestamp = new Date().getTime() / 1000; - let block = new Block(); + let block = new BlockHeader(); - assert.strictEqual(block.miner.value, "t01000"); - assert.strictEqual( - block.ticket.vrfProof, - "tPnuOjWp9LS/w5VuB+ALc0wn+0aNRF9SkOSykAszkppjnSYGY1qFhhI2fI7PvS39FufkkH8AKCqctU23D4EkAKqZvnMEp8eVjy528BPWE394/n2Z4pJCgjHau2bK26vN" - ); - assert.strictEqual( - block.electionProof.vrfProof, - "kQHqldOpdnmexjOh8KwzR6kjSGHAD6tWWM9DpTgf1e/FuxZXwB6lSXg9rlVyMk1OFbRbOOqvbHL5ZER/HTD3a3d3DTlmJ6T8H+oAqVTkh64hdoX2QTyL9EHymMIpgTKX" - ); + assert.strictEqual(block.miner, "t01000"); assert.strictEqual(block.beaconEntries.length, 0); assert.strictEqual(block.winPoStProof.length, 0); assert.strictEqual(block.parents.length, 0); - assert.strictEqual(block.parentWeight, 0); + assert.strictEqual(block.parentWeight, 0n); + + // The below verifies these CIDs point to 0 + let cid = new IPFSCid(block.parentStateRoot.root.value); + assert(multihashing.verify(cid.multihash, Buffer.from([0]))); + cid = new IPFSCid(block.parentMessageReceipts.root.value); + assert(multihashing.verify(cid.multihash, Buffer.from([0]))); + cid = new IPFSCid(block.messages.root.value); + assert(multihashing.verify(cid.multihash, Buffer.from([0]))); + assert.strictEqual(block.height, 0); - assert.strictEqual(block.parentStateRoot.length, 0); - assert.strictEqual(block.parentMessageReceipts.length, 0); - assert.strictEqual(block.messages.length, 0); - assert.strictEqual( - block.blsAggregate.data, - "wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - ); assert(block.timestamp >= timestamp); - assert.strictEqual( - block.blockSignature.data, - "t1vv8DSsC2vAVmJsEjVyZgLcYS4+AG0qQzViaVWhfdW24YOt7qkRuDxSftbis/ZlDgCc1sGom26PvnLKLe4H0qJP7B4wW3yw8vp0zovZUV9zW1QkpKGJgO7HIhFlQcg9" - ); assert.strictEqual(block.forkSignaling, 0); }); }); @@ -132,7 +124,7 @@ describe("things", () => { const expectedAddress = "t3vc4eetfk32n3tv5z55p73a2vm32pwxnqgr3jmpf7ssnwff6yh34bjc4vvarzivian5advbmvpmgw7ijxrboa"; - let address = new Address(privateKey); + const address = Address.fromPrivateKey(privateKey); assert.strictEqual(address.value, expectedAddress); }); diff --git a/src/chains/filecoin/filecoin/tsconfig.declarations.json b/src/chains/filecoin/filecoin/tsconfig.declarations.json new file mode 100644 index 0000000000..a06cf27bf4 --- /dev/null +++ b/src/chains/filecoin/filecoin/tsconfig.declarations.json @@ -0,0 +1,26 @@ +{ + "extends": "../../../tsconfig-base.json", + "compilerOptions": { + "outDir": "lib", + "composite": true, + "declaration": true, + "emitDeclarationOnly": true, + "declarationDir": "../types" + }, + "include": ["index.ts", "src/**/*"], + "typeRoots": ["./node_modules/@types", "./src/@types"], + "references": [ + { + "name": "@ganache/filecoin-options", + "path": "../options" + }, + { + "name": "@ganache/options", + "path": "../../../packages/options" + }, + { + "name": "@ganache/utils", + "path": "../../../packages/utils" + } + ] +} diff --git a/src/chains/filecoin/filecoin/tsconfig.json b/src/chains/filecoin/filecoin/tsconfig.json index 74aae99c3d..c182316ec1 100644 --- a/src/chains/filecoin/filecoin/tsconfig.json +++ b/src/chains/filecoin/filecoin/tsconfig.json @@ -1,7 +1,10 @@ { - "extends": "../../../../tsconfig.json", + "extends": "../../../tsconfig-base.json", "compilerOptions": { - "outDir": "lib" + "outDir": "lib", + "composite": true, + "strictNullChecks": true, + "noImplicitAny": true }, "include": ["index.ts", "src/**/*"], "typeRoots": ["./node_modules/@types", "./src/@types"], diff --git a/src/chains/filecoin/filecoin/tsconfig.test.json b/src/chains/filecoin/filecoin/tsconfig.test.json new file mode 100644 index 0000000000..726335a118 --- /dev/null +++ b/src/chains/filecoin/filecoin/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + // we can't use these in tests because the rest of the repo is broken + "strictNullChecks": false, + "noImplicitAny": false + } +} diff --git a/src/chains/filecoin/options/npm-shrinkwrap.json b/src/chains/filecoin/options/npm-shrinkwrap.json index 10a1256cd9..e3151318d9 100644 --- a/src/chains/filecoin/options/npm-shrinkwrap.json +++ b/src/chains/filecoin/options/npm-shrinkwrap.json @@ -4,6 +4,12 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/seedrandom": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.28.tgz", + "integrity": "sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA==", + "dev": true + }, "seedrandom": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", diff --git a/src/chains/filecoin/options/package.json b/src/chains/filecoin/options/package.json index cb3f6e5dd9..abcea7b1bf 100644 --- a/src/chains/filecoin/options/package.json +++ b/src/chains/filecoin/options/package.json @@ -21,9 +21,9 @@ "directory": "src/chains/filecoin/options" }, "scripts": { - "tsc": "ttsc", + "tsc": "ttsc --build", "test": "nyc npm run mocha", - "mocha": "cross-env TS_NODE_COMPILER=ttypescript TS_NODE_FILES=true mocha --exit --check-leaks --throw-deprecation --trace-warnings --require ts-node/register 'tests/**/*.test.ts'" + "mocha": "cross-env TS_NODE_COMPILER=ttypescript TS_NODE_FILES=true ts-node ../../../../scripts/lerna-mocha.ts --require ts-node/register '_PACKAGEDIR_/tests/**/*.test.ts'" }, "bugs": { "url": "https://github.com/trufflesuite/ganache-core/issues" @@ -46,5 +46,8 @@ "dependencies": { "@ganache/options": "0.1.0", "seedrandom": "3.0.5" + }, + "devDependencies": { + "@types/seedrandom": "2.4.28" } } diff --git a/src/chains/filecoin/options/src/chain-options.ts b/src/chains/filecoin/options/src/chain-options.ts index 1dc58181de..7bd291b452 100644 --- a/src/chains/filecoin/options/src/chain-options.ts +++ b/src/chains/filecoin/options/src/chain-options.ts @@ -4,18 +4,55 @@ import { Definitions } from "@ganache/options"; export type ChainConfig = { options: { /** + * The IPFS simulator host name/address to listen on. + * + * @default "127.0.0.1" + */ + readonly ipfsHost: { + type: string; + hasDefault: true; + }; + + /** + * The IPFS simulator port. + * * @default 5001 */ readonly ipfsPort: { type: number; hasDefault: true; }; + + /** + * When set to `false` only one request will be processed at a time. + * + * @default true + */ + readonly asyncRequestProcessing: { + type: boolean; + hasDefault: true; + }; }; }; export const ChainOptions: Definitions = { + ipfsHost: { + normalize, + cliDescription: "The IPFS simulator host name/address to listen on.", + default: () => "127.0.0.1", + cliType: "string" + }, ipfsPort: { normalize, - default: () => 5001 + cliDescription: "The IPFS simulator port.", + default: () => 5001, + cliType: "number" + }, + asyncRequestProcessing: { + normalize, + cliDescription: + "When set to `false` only one request will be processed at a time.", + default: () => true, + cliType: "boolean" } }; diff --git a/src/chains/filecoin/options/src/index.ts b/src/chains/filecoin/options/src/index.ts index 7ea8326ce7..268e8cf56a 100644 --- a/src/chains/filecoin/options/src/index.ts +++ b/src/chains/filecoin/options/src/index.ts @@ -6,7 +6,6 @@ import { WalletConfig, WalletOptions } from "./wallet-options"; import { Base, Defaults, - Definitions, ExternalConfig, InternalConfig, Legacy, @@ -17,13 +16,20 @@ import { OptionsConfig } from "@ganache/options"; -export type FilecoinOptions = { +type FilecoinConfig = { chain: ChainConfig; logging: LoggingConfig; miner: MinerConfig; wallet: WalletConfig; }; +export const FilecoinDefaults: Defaults = { + chain: ChainOptions, + logging: LoggingOptions, + miner: MinerOptions, + wallet: WalletOptions +}; + type MakeLegacyOptions = UnionToIntersection< { [K in OptionName]: K extends LegacyOptions @@ -38,7 +44,7 @@ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( ? I : never; -export type FilecoinLegacyOptions = Partial< +export type FilecoinLegacyProviderOptions = Partial< MakeLegacyOptions & MakeLegacyOptions & MakeLegacyOptions & @@ -47,23 +53,12 @@ export type FilecoinLegacyOptions = Partial< export type FilecoinProviderOptions = Partial< { - [K in keyof FilecoinOptions]: ExternalConfig; + [K in keyof FilecoinConfig]: ExternalConfig; } >; export type FilecoinInternalOptions = { - [K in keyof FilecoinOptions]: InternalConfig; -}; - -export type FilecoinDefaults = { - [K in keyof FilecoinOptions]: Definitions; -}; - -export const filecoinDefaults: Defaults = { - chain: ChainOptions, - logging: LoggingOptions, - miner: MinerOptions, - wallet: WalletOptions + [K in keyof FilecoinConfig]: InternalConfig; }; -export const FilecoinOptionsConfig = new OptionsConfig(filecoinDefaults); +export const FilecoinOptionsConfig = new OptionsConfig(FilecoinDefaults); diff --git a/src/chains/filecoin/options/src/logging-options.ts b/src/chains/filecoin/options/src/logging-options.ts index f9c72085d3..21f6f13380 100644 --- a/src/chains/filecoin/options/src/logging-options.ts +++ b/src/chains/filecoin/options/src/logging-options.ts @@ -34,11 +34,14 @@ export type LoggingConfig = { }; }; -const logger: Logger = { log: () => {} }; +const logger: Logger = { log: console.log }; export const LoggingOptions: Definitions = { logger: { normalize, + cliDescription: + "An object, like `console`, that implements a `log` function.", + disableInCLI: true, default: () => logger, legacyName: "logger" } diff --git a/src/chains/filecoin/options/src/miner-options.ts b/src/chains/filecoin/options/src/miner-options.ts index 618f5c51cf..8b8d6567b2 100644 --- a/src/chains/filecoin/options/src/miner-options.ts +++ b/src/chains/filecoin/options/src/miner-options.ts @@ -6,7 +6,8 @@ export type MinerConfig = { /** * Sets the `blockTime` in seconds for automatic mining. A blockTime of `0` * (default) enables "instamine mode", where new executable transactions - * will be mined instantly. + * will be mined instantly. A negative blockTime will require mining by + * manually calling Ganache.MineTipset. * * Using the `blockTime` option is discouraged unless you have tests which * require a specific mining interval. @@ -17,22 +18,15 @@ export type MinerConfig = { type: number; hasDefault: true; }; - - automining: { - type: boolean; - rawType: boolean; - hasDefault: true; - }; }; }; export const MinerOptions: Definitions = { blockTime: { normalize, - default: () => 0 - }, - automining: { - normalize, - default: () => true + cliDescription: + 'Sets the `blockTime` in seconds for automatic mining. A blockTime of `0` enables "instamine mode", where new executable transactions will be mined instantly.', + default: () => 0, + cliType: "number" } }; diff --git a/src/chains/filecoin/options/src/wallet-options.ts b/src/chains/filecoin/options/src/wallet-options.ts index 2ec0ea26ed..d53c7a88e1 100644 --- a/src/chains/filecoin/options/src/wallet-options.ts +++ b/src/chains/filecoin/options/src/wallet-options.ts @@ -1,7 +1,7 @@ import { normalize } from "./helpers"; import seedrandom from "seedrandom"; -import { Definitions } from "@ganache/options"; +import { Definitions, DeterministicSeedPhrase } from "@ganache/options"; const { alea } = seedrandom; @@ -26,18 +26,45 @@ export type OptionsAccount = { export type WalletConfig = { options: { /** - * Seed to use to generate a mnemonic + * Use pre-defined, deterministic seed. + */ + deterministic: { + type: boolean; + hasDefault: true; + }; + + /** + * Seed to use to generate a mnemonic. */ seed: { type: string; hasDefault: true; }; }; + exclusiveGroups: [["deterministic", "seed"]]; }; export const WalletOptions: Definitions = { + deterministic: { + normalize, + cliDescription: "Use pre-defined, deterministic seed.", + default: () => false, + cliAliases: ["d"], + cliType: "boolean", + conflicts: ["seed"] + }, seed: { normalize, - default: () => randomAlphaNumericString(10, alea()) + cliDescription: "Seed to use to generate a mnemonic.", + // The order of the options matter here! `wallet.deterministic` + // needs to be prior to `wallet.seed` for `config.deterministic` + // below to be set correctly + default: config => + config.deterministic + ? DeterministicSeedPhrase + : randomAlphaNumericString(10, alea()), + cliAliases: ["s"], + cliType: "string", + conflicts: ["deterministic"] } }; diff --git a/src/chains/filecoin/options/tsconfig.json b/src/chains/filecoin/options/tsconfig.json index 1e94df9a6c..dfa91289f0 100644 --- a/src/chains/filecoin/options/tsconfig.json +++ b/src/chains/filecoin/options/tsconfig.json @@ -1,10 +1,12 @@ { - "extends": "../../../../tsconfig.json", + "extends": "../../../tsconfig-base.json", "compilerOptions": { "outDir": "lib", - "composite": true + "composite": true, + "strictNullChecks": true, + "noImplicitAny": true }, - "include": ["src"], + "include": ["index.ts", "src"], "references": [ { "name": "@ganache/options", diff --git a/src/chains/filecoin/types/.npmignore b/src/chains/filecoin/types/.npmignore new file mode 100644 index 0000000000..ece9be561b --- /dev/null +++ b/src/chains/filecoin/types/.npmignore @@ -0,0 +1,6 @@ +tests +.nyc_output +coverage +scripts +tsconfig.json +typedoc.json diff --git a/src/chains/filecoin/types/LICENSE b/src/chains/filecoin/types/LICENSE new file mode 100644 index 0000000000..39f3b14498 --- /dev/null +++ b/src/chains/filecoin/types/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019-2020 Truffle Blockchain Group + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/chains/filecoin/types/README.md b/src/chains/filecoin/types/README.md new file mode 100644 index 0000000000..0aec59c49c --- /dev/null +++ b/src/chains/filecoin/types/README.md @@ -0,0 +1,3 @@ +# `@ganache/@ganache/filecoin-types` + +> TODO: description diff --git a/src/chains/filecoin/types/index.d.ts b/src/chains/filecoin/types/index.d.ts new file mode 100644 index 0000000000..70720547eb --- /dev/null +++ b/src/chains/filecoin/types/index.d.ts @@ -0,0 +1,8 @@ +/*! + * @ganache/filecoin + * + * @copyright Truffle Blockchain Group + * @author Tim Coulter + * @license MIT + */ +export * from "./src/connector"; diff --git a/src/chains/filecoin/types/npm-shrinkwrap.json b/src/chains/filecoin/types/npm-shrinkwrap.json new file mode 100644 index 0000000000..af62c04b75 --- /dev/null +++ b/src/chains/filecoin/types/npm-shrinkwrap.json @@ -0,0 +1 @@ +{"name":"@ganache/@ganache/filecoin-types","version":"0.1.0","lockfileVersion":1} diff --git a/src/chains/filecoin/types/package.json b/src/chains/filecoin/types/package.json new file mode 100644 index 0000000000..67be267851 --- /dev/null +++ b/src/chains/filecoin/types/package.json @@ -0,0 +1,36 @@ +{ + "name": "@ganache/filecoin-types", + "version": "0.1.0", + "description": "", + "author": "Tim Coulter", + "homepage": "https://github.com/trufflesuite/ganache-core/tree/develop/src/chains/filecoin/types#readme", + "license": "MIT", + "types": "index.d.ts", + "source": "index.ts", + "repository": { + "type": "git", + "url": "https://github.com/trufflesuite/ganache-core.git", + "directory": "src/chains/filecoin/types" + }, + "scripts": { + "tsc": "echo '@ganache/types does not get compiled directly; see @ganache/filecoin'" + }, + "bugs": { + "url": "https://github.com/trufflesuite/ganache-core/issues" + }, + "keywords": [ + "ganache", + "ganache-filecoin-types", + "ethereum", + "evm", + "blockchain", + "smart contracts", + "dapps", + "solidity", + "vyper", + "fe", + "web3", + "tooling", + "truffle" + ] +} diff --git a/src/chains/filecoin/types/src/api.d.ts b/src/chains/filecoin/types/src/api.d.ts new file mode 100644 index 0000000000..db057be43a --- /dev/null +++ b/src/chains/filecoin/types/src/api.d.ts @@ -0,0 +1,37 @@ +import { types, PromiEvent, Subscription } from "@ganache/utils"; +import Blockchain from "./blockchain"; +import { SerializedStartDealParams } from "./things/start-deal-params"; +import { SerializedRootCID } from "./things/root-cid"; +import { SerializedDealInfo } from "./things/deal-info"; +import { SerializedTipset } from "./things/tipset"; +import { SerializedAddress } from "./things/address"; +import { SerializedRetrievalOrder } from "./things/retrieval-order"; +import { SerializedQueryOffer } from "./things/query-offer"; +import { SubscriptionMethod, SubscriptionId } from "./types/subscriptions"; +export default class FilecoinApi implements types.Api { + #private; + readonly [index: string]: (...args: any) => Promise; + constructor(blockchain: Blockchain); + stop(): Promise; + "Filecoin.ChainGetGenesis"(): Promise; + "Filecoin.ChainHead"(): Promise; + "Filecoin.ChainNotify"(rpcId?: string): PromiEvent; + [SubscriptionMethod.ChannelClosed]( + subscriptionId: SubscriptionId + ): Promise; + "Filecoin.StateListMiners"(): Promise>; + "Filecoin.WalletDefaultAddress"(): Promise; + "Filecoin.WalletBalance"(address: string): Promise; + "Filecoin.ClientStartDeal"( + serializedProposal: SerializedStartDealParams + ): Promise; + "Filecoin.ClientListDeals"(): Promise>; + "Filecoin.ClientFindData"( + rootCid: SerializedRootCID + ): Promise>; + "Filecoin.ClientHasLocal"(rootCid: SerializedRootCID): Promise; + "Filecoin.ClientRetrieve"( + retrievalOffer: SerializedRetrievalOrder + ): Promise; + "Ganache.MineTipset"(): Promise; +} diff --git a/src/chains/filecoin/types/src/blockchain.d.ts b/src/chains/filecoin/types/src/blockchain.d.ts new file mode 100644 index 0000000000..a1bf5da046 --- /dev/null +++ b/src/chains/filecoin/types/src/blockchain.d.ts @@ -0,0 +1,49 @@ +import { Tipset } from "./things/tipset"; +import { RootCID } from "./things/root-cid"; +import Emittery from "emittery"; +import { Address } from "./things/address"; +import { DealInfo } from "./things/deal-info"; +import Balance from "./things/balance"; +import { StartDealParams } from "./things/start-deal-params"; +import { IPFSNode } from "./ipfs-server"; +import { RetrievalOrder } from "./things/retrieval-order"; +import { FilecoinInternalOptions } from "@ganache/filecoin-options"; +import { QueryOffer } from "./things/query-offer"; +export declare type BlockchainEvents = { + ready(): void; + tipset: Tipset; +}; +export default class Blockchain extends Emittery.Typed< + BlockchainEvents, + keyof BlockchainEvents +> { + #private; + readonly tipsets: Array; + readonly miner: string; + readonly address: Address; + get balance(): Balance; + readonly deals: Array; + readonly dealsByCid: Record; + readonly inProcessDeals: Array; + readonly options: FilecoinInternalOptions; + private ipfsServer; + private miningTimeout; + private rng; + private ready; + constructor(options: FilecoinInternalOptions); + waitForReady(): Promise; + /** + * Gracefully shuts down the blockchain service and all of its dependencies. + */ + stop(): Promise; + get ipfs(): IPFSNode | null; + genesisTipset(): Tipset; + latestTipset(): Tipset; + mineTipset(numNewBlocks?: number): Promise; + hasLocal(cid: string): Promise; + private getIPFSObjectSize; + startDeal(proposal: StartDealParams): Promise; + createQueryOffer(rootCid: RootCID): Promise; + retrieve(retrievalOrder: RetrievalOrder): Promise; + private logLatestTipset; +} diff --git a/src/chains/filecoin/types/src/connector.d.ts b/src/chains/filecoin/types/src/connector.d.ts new file mode 100644 index 0000000000..c90d122a4b --- /dev/null +++ b/src/chains/filecoin/types/src/connector.d.ts @@ -0,0 +1,40 @@ +/// +import Emittery from "emittery"; +import FilecoinApi from "./api"; +import { JsonRpcTypes, types, utils } from "@ganache/utils"; +import FilecoinProvider from "./provider"; +import { RecognizedString, HttpRequest, WebSocket } from "uWebSockets.js"; +import { FilecoinProviderOptions } from "@ganache/filecoin-options"; +export declare type Provider = FilecoinProvider; +export declare const Provider: typeof FilecoinProvider; +export declare class Connector + extends Emittery.Typed<{}, "ready" | "close"> + implements + types.Connector< + FilecoinApi, + JsonRpcTypes.Request, + JsonRpcTypes.Response + > { + #private; + get provider(): FilecoinProvider; + constructor( + providerOptions: FilecoinProviderOptions, + executor: utils.Executor + ); + parse(message: Buffer): JsonRpcTypes.Request; + handle( + payload: JsonRpcTypes.Request, + _connection: HttpRequest | WebSocket + ): Promise; + format( + result: any, + payload: JsonRpcTypes.Request + ): RecognizedString; + formatError( + error: Error & { + code: number; + }, + payload: JsonRpcTypes.Request + ): RecognizedString; + close(): Promise; +} diff --git a/src/chains/filecoin/types/src/database.d.ts b/src/chains/filecoin/types/src/database.d.ts new file mode 100644 index 0000000000..d8b003d1fd --- /dev/null +++ b/src/chains/filecoin/types/src/database.d.ts @@ -0,0 +1,4 @@ +export declare type DatabaseOptions = {}; +export default class Database implements DatabaseOptions { + constructor(options: DatabaseOptions); +} diff --git a/src/chains/filecoin/types/src/deal-state.d.ts b/src/chains/filecoin/types/src/deal-state.d.ts new file mode 100644 index 0000000000..d35b86932b --- /dev/null +++ b/src/chains/filecoin/types/src/deal-state.d.ts @@ -0,0 +1,26 @@ +export declare enum DealState { + Unknown = 0, + ProposalNotFound = 1, + ProposalRejected = 2, + ProposalAccepted = 3, + Staged = 4, + Sealing = 5, + Active = 6, + Failing = 7, + NotFound = 8, + FundsEnsured = 9, + Validating = 10, + Transferring = 11, + WaitingForData = 12, + VerifyData = 13, + EnsureProviderFunds = 14, + EnsureClientFunds = 15, + ProviderFunding = 16, + ClientFunding = 17, + Publish = 18, + Publishing = 19, + Error = 20, + Completed = 21 +} +export declare let terminalStates: Array; +export declare let nextSuccessfulState: Record; diff --git a/src/chains/filecoin/types/src/ipfs-server.d.ts b/src/chains/filecoin/types/src/ipfs-server.d.ts new file mode 100644 index 0000000000..60e84ae012 --- /dev/null +++ b/src/chains/filecoin/types/src/ipfs-server.d.ts @@ -0,0 +1,35 @@ +export declare type IPFSNode = { + apiAddr: { + toString(): string; + }; + stop(): Promise; + add( + data: any + ): Promise<{ + path: string; + }>; + object: { + stat( + key: string, + options: any + ): Promise<{ + BlockSize: number; + CumulativeSize: number; + DataSize: number; + Hash: string; + LinksSize: number; + NumLinks: number; + }>; + }; +}; +declare class IPFSServer { + static readonly DEFAULT_PORT = 5001; + readonly serverPort: number; + readonly apiPort: number; + node: IPFSNode | null; + private httpServer; + constructor(apiPort: number); + start(): Promise; + stop(): Promise; +} +export default IPFSServer; diff --git a/src/chains/filecoin/types/src/provider.d.ts b/src/chains/filecoin/types/src/provider.d.ts new file mode 100644 index 0000000000..096f85dd98 --- /dev/null +++ b/src/chains/filecoin/types/src/provider.d.ts @@ -0,0 +1,58 @@ +import Emittery from "emittery"; +import { PromiEvent, types, utils } from "@ganache/utils"; +import JsonRpc from "@ganache/utils/src/things/jsonrpc"; +import FilecoinApi from "./api"; +import { Schema } from "@filecoin-shipyard/lotus-client-schema"; +import Blockchain from "./blockchain"; +import { + FilecoinProviderOptions, + FilecoinInternalOptions +} from "@ganache/filecoin-options"; +export default class FilecoinProvider + extends Emittery.Typed<{}, "ready"> + implements types.Provider { + #private; + readonly blockchain: Blockchain; + static readonly Schema: Schema; + constructor(options: FilecoinProviderOptions, executor: utils.Executor); + /** + * Returns the options, including defaults and generated, used to start Ganache. + */ + getOptions(): FilecoinInternalOptions; + /** + * Returns the unlocked accounts + */ + getInitialAccounts(): Record< + string, + { + unlocked: boolean; + secretKey: string; + balance: bigint; + } + >; + connect(): Promise; + send(payload: JsonRpc.Request): Promise; + _requestRaw( + payload: JsonRpc.Request + ): Promise< + | { + value: PromiEvent; + } + | { + value: PromiseLike>; + } + >; + sendHttp(): Promise; + sendWs(): Promise; + sendSubscription( + payload: JsonRpc.Request, + schemaMethod: { + subscription?: boolean; + }, + subscriptionCallback: (data: any) => void + ): Promise<(Promise | (() => void))[]>; + receive(): Promise; + import(): Promise; + destroy(): Promise; + stop(): Promise; +} diff --git a/src/chains/filecoin/types/src/schema.d.ts b/src/chains/filecoin/types/src/schema.d.ts new file mode 100644 index 0000000000..52e0888743 --- /dev/null +++ b/src/chains/filecoin/types/src/schema.d.ts @@ -0,0 +1,3 @@ +import { Schema } from "@filecoin-shipyard/lotus-client-schema"; +declare const GanacheSchema: Schema; +export default GanacheSchema; diff --git a/src/chains/filecoin/types/src/things/address.d.ts b/src/chains/filecoin/types/src/things/address.d.ts new file mode 100644 index 0000000000..dbfdbbbe75 --- /dev/null +++ b/src/chains/filecoin/types/src/things/address.d.ts @@ -0,0 +1,45 @@ +/// +import { SerializableLiteral } from "./serializable-literal"; +import { StartDealParams } from "./start-deal-params"; +import { utils } from "@ganache/utils"; +interface AddressConfig { + type: string; +} +declare enum AddressProtocol { + ID = 0, + SECP256K1 = 1, + Actor = 2, + BLS = 3, + Unknown = 255 +} +declare enum AddressNetwork { + Testnet = "t", + Mainnet = "f", + Unknown = "UNKNOWN" +} +declare class Address extends SerializableLiteral { + #private; + get config(): {}; + static readonly CHECKSUM_BYTES = 4; + get privateKey(): string | undefined; + get network(): AddressNetwork; + get protocol(): AddressProtocol; + constructor(publicAddress: string, privateKey?: string); + setPrivateKey(privateKey: string): void; + signProposal(proposal: StartDealParams): Promise; + static fromPrivateKey( + privateKey: string, + protocol?: AddressProtocol, + network?: AddressNetwork + ): Address; + static random( + rng?: utils.RandomNumberGenerator, + protocol?: AddressProtocol, + network?: AddressNetwork + ): Address; + static isValid(value: string): boolean; + static parseNetwork(publicAddress: string): AddressNetwork; + static parseProtocol(publicAddress: string): AddressProtocol; +} +declare type SerializedAddress = string; +export { Address, SerializedAddress, AddressProtocol }; diff --git a/src/chains/filecoin/types/src/things/balance.d.ts b/src/chains/filecoin/types/src/things/balance.d.ts new file mode 100644 index 0000000000..b6572bcbb4 --- /dev/null +++ b/src/chains/filecoin/types/src/things/balance.d.ts @@ -0,0 +1,10 @@ +import { LiteralDefinition, SerializableLiteral } from "./serializable-literal"; +interface BalanceConfig { + type: bigint; +} +declare class Balance extends SerializableLiteral { + get config(): LiteralDefinition; + sub(val: string | number | bigint): Balance; + toFIL(): number; +} +export default Balance; diff --git a/src/chains/filecoin/types/src/things/beacon-entry.d.ts b/src/chains/filecoin/types/src/things/beacon-entry.d.ts new file mode 100644 index 0000000000..485fa46774 --- /dev/null +++ b/src/chains/filecoin/types/src/things/beacon-entry.d.ts @@ -0,0 +1,35 @@ +/// +import { + SerializableObject, + SerializedObject, + DeserializedObject, + Definitions +} from "./serializable-object"; +interface BeaconEntryConfig { + properties: { + round: { + type: number; + serializedType: number; + serializedName: "Round"; + }; + data: { + type: Buffer; + serializedType: string; + serializedName: "Data"; + }; + }; +} +declare class BeaconEntry + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + round: number; + data: Buffer; +} +declare type SerializedBeaconEntry = SerializedObject; +export { BeaconEntry, SerializedBeaconEntry }; diff --git a/src/chains/filecoin/types/src/things/block-header.d.ts b/src/chains/filecoin/types/src/things/block-header.d.ts new file mode 100644 index 0000000000..64bf248a32 --- /dev/null +++ b/src/chains/filecoin/types/src/things/block-header.d.ts @@ -0,0 +1,129 @@ +import { Ticket, SerializedTicket } from "./ticket"; +import { ElectionProof, SerializedElectionProof } from "./election-proof"; +import { BeaconEntry, SerializedBeaconEntry } from "./beacon-entry"; +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +import { PoStProof, SerializedPoStProof } from "./post-proof"; +import { RootCID, SerializedRootCID } from "./root-cid"; +import { CID } from "./cid"; +import { SerializedSignature, Signature } from "./signature"; +interface BlockHeaderConfig { + properties: { + miner: { + type: string; + serializedType: string; + serializedName: "Miner"; + }; + ticket: { + type: Ticket; + serializedType: SerializedTicket; + serializedName: "Ticket"; + }; + electionProof: { + type: ElectionProof; + serializedType: SerializedElectionProof; + serializedName: "ElectionProof"; + }; + beaconEntries: { + type: Array; + serializedType: Array; + serializedName: "BeaconEntries"; + }; + winPoStProof: { + type: Array; + serializedType: Array; + serializedName: "WinPoStProof"; + }; + parents: { + type: Array; + serializedType: Array; + serializedName: "Parents"; + }; + parentWeight: { + type: bigint; + serializedType: string; + serializedName: "ParentWeight"; + }; + height: { + type: number; + serializedType: number; + serializedName: "Height"; + }; + parentStateRoot: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "ParentStateRoot"; + }; + parentMessageReceipts: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "ParentMessageReceipts"; + }; + messages: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "Messages"; + }; + blsAggregate: { + type: Signature; + serializedType: SerializedSignature; + serializedName: "BLSAggregate"; + }; + timestamp: { + type: number; + serializedType: number; + serializedName: "Timestamp"; + }; + blockSignature: { + type: Signature; + serializedType: SerializedSignature; + serializedName: "BlockSig"; + }; + forkSignaling: { + type: 0 | 1; + serializedType: 0 | 1; + serializedName: "ForkSignaling"; + }; + parentBaseFee: { + type: bigint; + serializedType: string; + serializedName: "ParentBaseFee"; + }; + }; +} +declare class BlockHeader + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + miner: string; + ticket: Ticket; + electionProof: ElectionProof; + beaconEntries: Array; + winPoStProof: Array; + parents: Array; + parentWeight: bigint; + height: number; + parentStateRoot: RootCID; + parentMessageReceipts: RootCID; + messages: RootCID; + blsAggregate: Signature; + /** + * Timestamp in seconds. Reference implementation: https://git.io/Jt3HJ. + */ + timestamp: number; + blockSignature: Signature; + forkSignaling: 0 | 1; + parentBaseFee: bigint; + get cid(): CID; +} +declare type SerializedBlockHeader = SerializedObject; +export { BlockHeader, SerializedBlockHeader }; diff --git a/src/chains/filecoin/filecoin/src/things/block-signature.ts b/src/chains/filecoin/types/src/things/block-signature.d.ts similarity index 54% rename from src/chains/filecoin/filecoin/src/things/block-signature.ts rename to src/chains/filecoin/types/src/things/block-signature.d.ts index 4f9c2de567..f5321f27db 100644 --- a/src/chains/filecoin/filecoin/src/things/block-signature.ts +++ b/src/chains/filecoin/types/src/things/block-signature.d.ts @@ -4,7 +4,6 @@ import { Definitions, SerializedObject } from "./serializable-object"; - interface BlockSignatureConfig { properties: { type: { @@ -19,28 +18,12 @@ interface BlockSignatureConfig { }; }; } - -class BlockSignature +declare class BlockSignature extends SerializableObject implements DeserializedObject { - get config(): Definitions { - return { - type: { - serializedName: "Type", - defaultValue: 2 - }, - data: { - serializedName: "Data", - defaultValue: - "t1vv8DSsC2vAVmJsEjVyZgLcYS4+AG0qQzViaVWhfdW24YOt7qkRuDxSftbis/ZlDgCc1sGom26PvnLKLe4H0qJP7B4wW3yw8vp0zovZUV9zW1QkpKGJgO7HIhFlQcg9" - } - }; - } - + get config(): Definitions; type: number; data: string; } - -type SerializedBlockSignature = SerializedObject; - +declare type SerializedBlockSignature = SerializedObject; export { BlockSignature, SerializedBlockSignature }; diff --git a/src/chains/filecoin/filecoin/src/things/block.ts b/src/chains/filecoin/types/src/things/block.d.ts similarity index 51% rename from src/chains/filecoin/filecoin/src/things/block.ts rename to src/chains/filecoin/types/src/things/block.d.ts index 558b987851..982300acca 100644 --- a/src/chains/filecoin/filecoin/src/things/block.ts +++ b/src/chains/filecoin/types/src/things/block.d.ts @@ -13,11 +13,6 @@ import { WinPoStProof, SerializedWinPoStProof } from "./win-post-proof"; import { RootCID, SerializedRootCID } from "./root-cid"; import { Miner } from "./miner"; import { CID } from "./cid"; -import cbor from "borc"; -import IPFSCid from "cids"; -import multihashing from "multihashing"; -import multicodec from "multicodec"; - interface BlockConfig { properties: { miner: { @@ -97,83 +92,10 @@ interface BlockConfig { }; }; } - -class Block +declare class Block extends SerializableObject implements DeserializedObject { - get config(): Definitions { - return { - miner: { - serializedName: "Miner", - defaultValue: options => new Miner(options) - }, - ticket: { - serializedName: "Ticket", - defaultValue: options => new Ticket(options) - }, - electionProof: { - serializedName: "ElectionProof", - defaultValue: options => new ElectionProof(options) - }, - beaconEntries: { - serializedName: "BeaconEntries", - defaultValue: options => - options ? options.map(entry => new BeaconEntry(entry)) : [] - }, - winPoStProof: { - serializedName: "WinPoStProof", - defaultValue: options => - options ? options.map(proof => new WinPoStProof(proof)) : [] - }, - parents: { - serializedName: "Parents", - defaultValue: options => - options ? options.map(parent => new RootCID(parent)) : [] - }, - parentWeight: { - serializedName: "ParentWeight", - defaultValue: 0 - }, - height: { - serializedName: "Height", - defaultValue: 0 - }, - parentStateRoot: { - serializedName: "ParentStateRoot", - defaultValue: options => - options ? options.map(parent => new RootCID(parent)) : [] - }, - parentMessageReceipts: { - serializedName: "ParentMessageReceipts", - defaultValue: options => - options ? options.map(parent => new RootCID(parent)) : [] - }, - messages: { - serializedName: "Messages", - defaultValue: options => - options ? options.map(parent => new RootCID(parent)) : [] - }, - blsAggregate: { - serializedName: "BLSAggregate", - defaultValue: options => new BLSAggregate(options) - }, - timestamp: { - serializedName: "Timestamp", - defaultValue: () => { - return new Date().getTime(); - } - }, - blockSignature: { - serializedName: "BlockSig", - defaultValue: options => new BlockSignature(options) - }, - forkSignaling: { - serializedName: "ForkSignaling", - defaultValue: 0 - } - }; - } - + get config(): Definitions; miner: Miner; ticket: Ticket; electionProof: ElectionProof; @@ -189,30 +111,7 @@ class Block timestamp: number; blockSignature: BlockSignature; forkSignaling: 0 | 1; - - get cid(): CID { - let blockHeader: Partial> = {}; - - for (const [deserializedName, { serializedName }] of Object.entries( - this.config - )) { - blockHeader[serializedName] = this[deserializedName]; - } - - // We could have used the ipld-dag-cbor package for the following, - // but it was async, which caused a number of issues during object construction. - let cborBlockHeader = cbor.encode(blockHeader); - let multihash = multihashing(cborBlockHeader, "blake2b-256"); - let rawCid = new IPFSCid( - 1, - multicodec.print[multicodec.DAG_CBOR], - multihash - ); - - return new CID(rawCid.toString()); - } + get cid(): CID; } - -type SerializedBlock = SerializedObject; - +declare type SerializedBlock = SerializedObject; export { Block, SerializedBlock }; diff --git a/src/chains/filecoin/filecoin/src/things/bls-aggregate.ts b/src/chains/filecoin/types/src/things/bls-aggregate.d.ts similarity index 54% rename from src/chains/filecoin/filecoin/src/things/bls-aggregate.ts rename to src/chains/filecoin/types/src/things/bls-aggregate.d.ts index d8a62a024d..4689d7c2fa 100644 --- a/src/chains/filecoin/filecoin/src/things/bls-aggregate.ts +++ b/src/chains/filecoin/types/src/things/bls-aggregate.d.ts @@ -4,7 +4,6 @@ import { Definitions, SerializedObject } from "./serializable-object"; - interface BLSAggregateConfig { properties: { type: { @@ -19,28 +18,12 @@ interface BLSAggregateConfig { }; }; } - -class BLSAggregate +declare class BLSAggregate extends SerializableObject implements DeserializedObject { - get config(): Definitions { - return { - type: { - serializedName: "Type", - defaultValue: 2 - }, - data: { - serializedName: "Data", - defaultValue: - "wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - } - }; - } - + get config(): Definitions; type: number; data: string; } - -type SerializedBLSAggregate = SerializedObject; - +declare type SerializedBLSAggregate = SerializedObject; export { BLSAggregate, SerializedBLSAggregate }; diff --git a/src/chains/filecoin/types/src/things/channel-id.d.ts b/src/chains/filecoin/types/src/things/channel-id.d.ts new file mode 100644 index 0000000000..d4290dd277 --- /dev/null +++ b/src/chains/filecoin/types/src/things/channel-id.d.ts @@ -0,0 +1,40 @@ +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +declare type ChannelIDConfig = { + properties: { + initiator: { + type: string; + serializedType: string; + serializedName: "Initiator"; + }; + responder: { + type: string; + serializedType: string; + serializedName: "Responder"; + }; + id: { + type: number; + serializedType: number; + serializedName: "ID"; + }; + }; +}; +declare class ChannelID + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + initiator: string; + responder: string; + id: number; +} +declare type SerializedChannelID = SerializedObject; +export { ChannelID, SerializedChannelID }; diff --git a/src/chains/filecoin/types/src/things/cid.d.ts b/src/chains/filecoin/types/src/things/cid.d.ts new file mode 100644 index 0000000000..2a26c7ee6a --- /dev/null +++ b/src/chains/filecoin/types/src/things/cid.d.ts @@ -0,0 +1,11 @@ +import { SerializableLiteral } from "./serializable-literal"; +interface CIDConfig { + type: string; +} +declare class CID extends SerializableLiteral { + get config(): {}; + static isValid(value: string): boolean; + static nullCID(): CID; +} +declare type SerializedCID = string; +export { CID, SerializedCID }; diff --git a/src/chains/filecoin/types/src/things/data-transfer-channel.d.ts b/src/chains/filecoin/types/src/things/data-transfer-channel.d.ts new file mode 100644 index 0000000000..cf6323a7e4 --- /dev/null +++ b/src/chains/filecoin/types/src/things/data-transfer-channel.d.ts @@ -0,0 +1,77 @@ +import { RootCID, SerializedRootCID } from "./root-cid"; +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +declare type DataTransferChannelConfig = { + properties: { + transferId: { + type: number; + serializedType: number; + serializedName: "TransferID"; + }; + status: { + type: number; + serializedType: number; + serializedName: "Status"; + }; + baseCID: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "BaseCID"; + }; + isInitiator: { + type: boolean; + serializedType: boolean; + serializedName: "IsInitiator"; + }; + isSender: { + type: boolean; + serializedType: boolean; + serializedName: "IsSender"; + }; + voucher: { + type: string; + serializedType: string; + serializedName: "Voucher"; + }; + message: { + type: string; + serializedType: string; + serializedName: "Message"; + }; + otherPeer: { + type: string; + serializedType: string; + serializedName: "OtherPeer"; + }; + transferred: { + type: number; + serializedType: number; + serializedName: "Transferred"; + }; + }; +}; +declare class DataTransferChannel + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + transferId: number; + status: number; + baseCID: RootCID; + isInitiator: boolean; + isSender: boolean; + voucher: string; + message: string; + otherPeer: string; + transferred: number; +} +declare type SerializedDataTransferChannel = SerializedObject; +export { DataTransferChannel, SerializedDataTransferChannel }; diff --git a/src/chains/filecoin/types/src/things/deal-info.d.ts b/src/chains/filecoin/types/src/things/deal-info.d.ts new file mode 100644 index 0000000000..f3fd0623c7 --- /dev/null +++ b/src/chains/filecoin/types/src/things/deal-info.d.ts @@ -0,0 +1,118 @@ +import { RootCID, SerializedRootCID } from "./root-cid"; +import { StorageDealStatus } from "../types/storage-deal-status"; +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +import { + SerializedStorageMarketDataRef, + StorageMarketDataRef +} from "./storage-market-data-ref"; +import { ChannelID, SerializedChannelID } from "./channel-id"; +import { + DataTransferChannel, + SerializedDataTransferChannel +} from "./data-transfer-channel"; +declare type DealInfoConfig = { + properties: { + proposalCid: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "ProposalCid"; + }; + state: { + type: StorageDealStatus; + serializedType: StorageDealStatus; + serializedName: "State"; + }; + message: { + type: string; + serializedType: string; + serializedName: "Message"; + }; + provider: { + type: string; + serializedType: string; + serializedName: "Provider"; + }; + dataRef: { + type: StorageMarketDataRef; + serializedType: SerializedStorageMarketDataRef; + serializedName: "DataRef"; + }; + pieceCid: { + type: RootCID | null; + serializedType: SerializedRootCID | null; + serializedName: "PieceCID"; + }; + size: { + type: number; + serializedType: number; + serializedName: "Size"; + }; + pricePerEpoch: { + type: bigint; + serializedType: string; + serializedName: "PricePerEpoch"; + }; + duration: { + type: number; + serializedType: number; + serializedName: "Duration"; + }; + dealId: { + type: number; + serializedType: number; + serializedName: "DealID"; + }; + creationTime: { + type: Date; + serializedType: string; + serializedName: "CreationTime"; + }; + verified: { + type: boolean; + serializedType: boolean; + serializedName: "Verified"; + }; + transferChannelId: { + type: ChannelID; + serializedType: SerializedChannelID; + serializedName: "TransferChannelID"; + }; + dataTransfer: { + type: DataTransferChannel; + serializedType: SerializedDataTransferChannel; + serializedName: "DataTransfer"; + }; + }; +}; +declare class DealInfo + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + proposalCid: RootCID; + state: StorageDealStatus; + message: string; + provider: string; + dataRef: StorageMarketDataRef; + pieceCid: RootCID | null; + size: number; + pricePerEpoch: bigint; + duration: number; + dealId: number; + creationTime: Date; + verified: boolean; + transferChannelId: ChannelID; + dataTransfer: DataTransferChannel; + advanceState(): void; +} +declare type SerializedDealInfo = SerializedObject; +export { DealInfo, SerializedDealInfo }; diff --git a/src/chains/filecoin/filecoin/src/things/deal.ts b/src/chains/filecoin/types/src/things/deal.d.ts similarity index 58% rename from src/chains/filecoin/filecoin/src/things/deal.ts rename to src/chains/filecoin/types/src/things/deal.d.ts index ad9cb095be..fd3fe45534 100644 --- a/src/chains/filecoin/filecoin/src/things/deal.ts +++ b/src/chains/filecoin/types/src/things/deal.d.ts @@ -1,5 +1,5 @@ import { RootCID, SerializedRootCID } from "./root-cid"; -import { DealState, nextSuccessfulState } from "../deal-state"; +import { DealState } from "../deal-state"; import { Miner, SerializedMiner } from "./miner"; import { SerializableObject, @@ -7,8 +7,7 @@ import { Definitions, SerializedObject } from "./serializable-object"; - -type DealConfig = { +declare type DealConfig = { properties: { proposalCid: { type: RootCID; @@ -17,7 +16,7 @@ type DealConfig = { }; state: { type: DealState; - serializedType: DealState; // Remember: Enums are numbers at runtime!, + serializedType: DealState; serializedName: "State"; }; message: { @@ -57,44 +56,10 @@ type DealConfig = { }; }; }; - -class Deal +declare class Deal extends SerializableObject implements DeserializedObject { - get config(): Definitions { - return { - proposalCid: { - serializedName: "ProposalCid", - defaultValue: options => new RootCID(options) - }, - state: { - serializedName: "State" - }, - message: { - serializedName: "Message" - }, - provider: { - serializedName: "Provider", - defaultValue: options => new Miner(options) - }, - pieceCid: { - serializedName: "PieceCID" - }, - size: { - serializedName: "Size" - }, - pricePerEpoch: { - serializedName: "PricePerEpoch" - }, - duration: { - serializedName: "Duration" - }, - dealId: { - serializedName: "DealID" - } - }; - } - + get config(): Definitions; proposalCid: RootCID; state: DealState; message: string; @@ -104,12 +69,7 @@ class Deal pricePerEpoch: string; duration: number; dealId: number; - - advanceState(fullyAdvance: boolean = false) { - this.state = nextSuccessfulState[this.state]; - } + advanceState(): void; } - -type SerializedDeal = SerializedObject; - +declare type SerializedDeal = SerializedObject; export { Deal, SerializedDeal }; diff --git a/src/chains/filecoin/types/src/things/election-proof.d.ts b/src/chains/filecoin/types/src/things/election-proof.d.ts new file mode 100644 index 0000000000..080b9d81f4 --- /dev/null +++ b/src/chains/filecoin/types/src/things/election-proof.d.ts @@ -0,0 +1,35 @@ +/// +import { + SerializableObject, + DeserializedObject, + SerializedObject, + Definitions +} from "./serializable-object"; +interface ElectionProofConfig { + properties: { + winCount: { + type: number; + serializedType: number; + serializedName: "WinCount"; + }; + vrfProof: { + type: Buffer; + serializedType: string; + serializedName: "VRFProof"; + }; + }; +} +declare class ElectionProof + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + winCount: number; + vrfProof: Buffer; +} +declare type SerializedElectionProof = SerializedObject; +export { ElectionProof, SerializedElectionProof }; diff --git a/src/chains/filecoin/types/src/things/head-change.d.ts b/src/chains/filecoin/types/src/things/head-change.d.ts new file mode 100644 index 0000000000..3e95268f1a --- /dev/null +++ b/src/chains/filecoin/types/src/things/head-change.d.ts @@ -0,0 +1,40 @@ +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +import { Tipset, SerializedTipset } from "./tipset"; +interface HeadChangeConfig { + properties: { + type: { + type: string; + serializedType: string; + serializedName: "Type"; + }; + val: { + type: Tipset; + serializedType: SerializedTipset; + serializedName: "Val"; + }; + }; +} +declare class HeadChange + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + type: string; + val: Tipset; +} +declare type SerializedHeadChange = SerializedObject; +export declare enum HeadChangeType { + HCRevert = "revert", + HCApply = "apply", + HCCurrent = "current" +} +export { HeadChange, SerializedHeadChange }; diff --git a/src/chains/filecoin/types/src/things/miner.d.ts b/src/chains/filecoin/types/src/things/miner.d.ts new file mode 100644 index 0000000000..aa00a20700 --- /dev/null +++ b/src/chains/filecoin/types/src/things/miner.d.ts @@ -0,0 +1,11 @@ +import { SerializableLiteral } from "./serializable-literal"; +interface MinerConfig { + type: string; +} +declare class Miner extends SerializableLiteral { + get config(): { + defaultValue: (literal: any) => any; + }; +} +declare type SerializedMiner = string; +export { Miner, SerializedMiner }; diff --git a/src/chains/filecoin/types/src/things/post-proof.d.ts b/src/chains/filecoin/types/src/things/post-proof.d.ts new file mode 100644 index 0000000000..4307b38e64 --- /dev/null +++ b/src/chains/filecoin/types/src/things/post-proof.d.ts @@ -0,0 +1,35 @@ +/// +import { + SerializableObject, + DeserializedObject, + SerializedObject, + Definitions +} from "./serializable-object"; +interface PoStProofConfig { + properties: { + postProof: { + type: number; + serializedType: number; + serializedName: "PoStProof"; + }; + proofBytes: { + type: Buffer; + serializedType: string; + serializedName: "ProofBytes"; + }; + }; +} +declare class PoStProof + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + postProof: number; + proofBytes: Buffer; +} +declare type SerializedPoStProof = SerializedObject; +export { PoStProof, SerializedPoStProof }; diff --git a/src/chains/filecoin/types/src/things/query-offer.d.ts b/src/chains/filecoin/types/src/things/query-offer.d.ts new file mode 100644 index 0000000000..bdffa3132e --- /dev/null +++ b/src/chains/filecoin/types/src/things/query-offer.d.ts @@ -0,0 +1,84 @@ +import { RootCID, SerializedRootCID } from "./root-cid"; +import { + SerializableObject, + SerializedObject, + DeserializedObject, + Definitions +} from "./serializable-object"; +import { RetrievalPeer, SerializedRetrievalPeer } from "./retrieval-peer"; +declare type QueryOfferConfig = { + properties: { + err: { + type: string; + serializedType: string; + serializedName: "Err"; + }; + root: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "Root"; + }; + piece: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "Piece"; + }; + size: { + type: number; + serializedType: number; + serializedName: "Size"; + }; + minPrice: { + type: bigint; + serializedType: string; + serializedName: "MinPrice"; + }; + unsealPrice: { + type: bigint; + serializedType: string; + serializedName: "UnsealPrice"; + }; + paymentInterval: { + type: number; + serializedType: number; + serializedName: "PaymentInterval"; + }; + paymentIntervalIncrease: { + type: number; + serializedType: number; + serializedName: "PaymentIntervalIncrease"; + }; + miner: { + type: string; + serializedType: string; + serializedName: "Miner"; + }; + minerPeer: { + type: RetrievalPeer; + serializedType: SerializedRetrievalPeer; + serializedName: "MinerPeer"; + }; + }; +}; +declare class QueryOffer + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + err: string; + root: RootCID; + piece: RootCID; + size: number; + minPrice: bigint; + unsealPrice: bigint; + paymentInterval: number; + paymentIntervalIncrease: number; + miner: string; + minerPeer: RetrievalPeer; +} +declare type SerializedQueryOffer = SerializedObject; +export { QueryOffer, SerializedQueryOffer }; diff --git a/src/chains/filecoin/filecoin/src/things/retrieval-offer.ts b/src/chains/filecoin/types/src/things/retrieval-offer.d.ts similarity index 56% rename from src/chains/filecoin/filecoin/src/things/retrieval-offer.ts rename to src/chains/filecoin/types/src/things/retrieval-offer.d.ts index 4867652952..a376ebdd7c 100644 --- a/src/chains/filecoin/filecoin/src/things/retrieval-offer.ts +++ b/src/chains/filecoin/types/src/things/retrieval-offer.d.ts @@ -6,8 +6,7 @@ import { DeserializedObject, Definitions } from "./serializable-object"; - -type RetrievalOfferConfig = { +declare type RetrievalOfferConfig = { properties: { err: { type: string; @@ -51,51 +50,10 @@ type RetrievalOfferConfig = { }; }; }; - -class RetrievalOffer +declare class RetrievalOffer extends SerializableObject implements DeserializedObject { - get config(): Definitions { - return { - err: { - serializedName: "Err", - defaultValue: "" - }, - root: { - serializedName: "Root", - defaultValue: options => new RootCID(options) - }, - size: { - serializedName: "Size" - }, - minPrice: { - serializedName: "MinPrice" - }, - paymentInterval: { - serializedName: "PaymentInterval", - defaultValue: 1048576 - }, - paymentIntervalIncrease: { - serializedName: "PaymentIntervalIncrease", - defaultValue: 1048576 - }, - miner: { - serializedName: "Miner" - }, - minerPeerId: { - serializedName: "MinerPeerID", - defaultValue: options => { - let alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"; - return " " - .repeat(52) - .split("") - .map(() => alphabet[Math.floor(Math.random() * alphabet.length)]) - .join(""); - } - } - }; - } - + get config(): Definitions; err: string; root: RootCID; size: number; @@ -105,7 +63,5 @@ class RetrievalOffer miner: Miner; minerPeerId: string; } - -type SerializedRetrievalOffer = SerializedObject; - +declare type SerializedRetrievalOffer = SerializedObject; export { RetrievalOffer, SerializedRetrievalOffer }; diff --git a/src/chains/filecoin/types/src/things/retrieval-order.d.ts b/src/chains/filecoin/types/src/things/retrieval-order.d.ts new file mode 100644 index 0000000000..1e67b155e7 --- /dev/null +++ b/src/chains/filecoin/types/src/things/retrieval-order.d.ts @@ -0,0 +1,84 @@ +import { RootCID, SerializedRootCID } from "./root-cid"; +import { + SerializableObject, + SerializedObject, + DeserializedObject, + Definitions +} from "./serializable-object"; +import { RetrievalPeer, SerializedRetrievalPeer } from "./retrieval-peer"; +declare type RetrievalOrderConfig = { + properties: { + root: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "Root"; + }; + piece: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "Piece"; + }; + size: { + type: number; + serializedType: number; + serializedName: "Size"; + }; + total: { + type: bigint; + serializedType: string; + serializedName: "Total"; + }; + unsealPrice: { + type: bigint; + serializedType: string; + serializedName: "UnsealPrice"; + }; + paymentInterval: { + type: number; + serializedType: number; + serializedName: "PaymentInterval"; + }; + paymentIntervalIncrease: { + type: number; + serializedType: number; + serializedName: "PaymentIntervalIncrease"; + }; + client: { + type: string; + serializedType: string; + serializedName: "Client"; + }; + miner: { + type: string; + serializedType: string; + serializedName: "Miner"; + }; + minerPeer: { + type: RetrievalPeer; + serializedType: SerializedRetrievalPeer; + serializedName: "MinerPeer"; + }; + }; +}; +declare class RetrievalOrder + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + root: RootCID; + piece: RootCID; + size: number; + total: bigint; + unsealPrice: bigint; + paymentInterval: number; + paymentIntervalIncrease: number; + client: string; + miner: string; + minerPeer: RetrievalPeer; +} +declare type SerializedRetrievalOrder = SerializedObject; +export { RetrievalOrder, SerializedRetrievalOrder }; diff --git a/src/chains/filecoin/types/src/things/retrieval-peer.d.ts b/src/chains/filecoin/types/src/things/retrieval-peer.d.ts new file mode 100644 index 0000000000..dc97e7dc47 --- /dev/null +++ b/src/chains/filecoin/types/src/things/retrieval-peer.d.ts @@ -0,0 +1,41 @@ +import { + SerializableObject, + SerializedObject, + DeserializedObject, + Definitions +} from "./serializable-object"; +import { RootCID, SerializedRootCID } from "./root-cid"; +declare type RetrievalPeerConfig = { + properties: { + address: { + type: string; + serializedType: string; + serializedName: "Address"; + }; + id: { + type: string; + serializedType: string; + serializedName: "ID"; + }; + pieceCID: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "PieceCID"; + }; + }; +}; +declare class RetrievalPeer + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + address: string; + id: string; + pieceCID: RootCID; +} +declare type SerializedRetrievalPeer = SerializedObject; +export { RetrievalPeer, SerializedRetrievalPeer }; diff --git a/src/chains/filecoin/types/src/things/root-cid.d.ts b/src/chains/filecoin/types/src/things/root-cid.d.ts new file mode 100644 index 0000000000..71c9502498 --- /dev/null +++ b/src/chains/filecoin/types/src/things/root-cid.d.ts @@ -0,0 +1,30 @@ +import { CID, SerializedCID } from "./cid"; +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +interface RootCIDConfig { + properties: { + root: { + type: CID; + serializedType: SerializedCID; + serializedName: "/"; + }; + }; +} +declare class RootCID + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + asPath(): string; + root: CID; +} +declare type SerializedRootCID = SerializedObject; +export { RootCID, SerializedRootCID }; diff --git a/src/chains/filecoin/types/src/things/serializable-literal.d.ts b/src/chains/filecoin/types/src/things/serializable-literal.d.ts new file mode 100644 index 0000000000..9e30806e14 --- /dev/null +++ b/src/chains/filecoin/types/src/things/serializable-literal.d.ts @@ -0,0 +1,25 @@ +/// +import { Serializable } from "./serializable-object"; +declare type BaseConfig = { + type: number | string | Buffer | bigint | null; +}; +declare type Literal = C["type"]; +declare type SerializedLiteral = C["type"] extends + | bigint + | Buffer + ? string + : Literal; +declare type DefaultValue = D | ((options: S | undefined) => D); +declare type LiteralDefinition = { + defaultValue?: DefaultValue, Literal>; +}; +declare abstract class SerializableLiteral + implements Serializable> { + protected abstract get config(): LiteralDefinition; + value: Literal; + constructor(literal?: SerializedLiteral); + private initialize; + serialize(): SerializedLiteral; + equals(obj: Serializable>): boolean; +} +export { SerializableLiteral, LiteralDefinition, Literal }; diff --git a/src/chains/filecoin/types/src/things/serializable-object.d.ts b/src/chains/filecoin/types/src/things/serializable-object.d.ts new file mode 100644 index 0000000000..dce6e1f975 --- /dev/null +++ b/src/chains/filecoin/types/src/things/serializable-object.d.ts @@ -0,0 +1,85 @@ +declare type BaseConfig = { + properties: { + [deserializedName: string]: { + type: any; + serializedName: string; + serializedType: any; + }; + }; +}; +declare type PropertyName = string & + keyof C["properties"]; +declare type SerializedPropertyName< + C extends BaseConfig, + N extends PropertyName = PropertyName +> = C["properties"][N]["serializedName"]; +declare type PropertyType< + C extends BaseConfig, + N extends PropertyName = PropertyName +> = C["properties"][N]["type"]; +declare type SerializedPropertyType< + C extends BaseConfig, + N extends PropertyName = PropertyName +> = C["properties"][N]["serializedType"]; +declare type DeserializedObject = { + [N in PropertyName]: PropertyType; +}; +declare type SerializedObjectWrapper = { + [N in PropertyName]: { + [S in SerializedPropertyName]: SerializedPropertyType; + }; +}; +interface Wrapper { + [propertyName: string]: { + [serializedPropertyName: string]: any; + }; +} +declare type Values = W[keyof W]; +declare type UnionToIntersection = ( + U extends any ? (k: U) => void : never +) extends (k: infer I) => void + ? I + : never; +declare type FlattenUnion = { + [K in keyof UnionToIntersection]: K extends keyof T + ? T[K] extends any[] + ? T[K] + : T[K] extends object + ? FlattenUnion + : T[K] + : UnionToIntersection[K]; +}; +declare type SerializedObject = FlattenUnion< + Values> +>; +declare type DefaultValue = D | ((options?: S) => D); +declare type Definition> = { + deserializedName: N; + serializedName: SerializedPropertyName; + defaultValue: DefaultValue, SerializedPropertyType>; +}; +declare type Definitions = { + [N in PropertyName]: Definition; +}; +interface Serializable { + serialize(): C; + equals(obj: Serializable): boolean; +} +declare abstract class SerializableObject + implements Serializable> { + protected abstract get config(): Definitions; + initializeValue>( + valueConfig: Definition, + options?: Partial> | Partial> + ): PropertyType; + private serializeValue; + serialize(): SerializedObject; + equals(obj: Serializable>): boolean; +} +export { + Serializable, + SerializableObject, + SerializedObject, + DeserializedObject, + Definitions +}; diff --git a/src/chains/filecoin/types/src/things/sig-type.d.ts b/src/chains/filecoin/types/src/things/sig-type.d.ts new file mode 100644 index 0000000000..e8219a92cf --- /dev/null +++ b/src/chains/filecoin/types/src/things/sig-type.d.ts @@ -0,0 +1,5 @@ +export declare enum SigType { + SigTypeUnknown = 255, + SigTypeSecp256k1 = 1, + SigTypeBLS = 2 +} diff --git a/src/chains/filecoin/types/src/things/signature.d.ts b/src/chains/filecoin/types/src/things/signature.d.ts new file mode 100644 index 0000000000..2184e30cba --- /dev/null +++ b/src/chains/filecoin/types/src/things/signature.d.ts @@ -0,0 +1,35 @@ +/// +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +interface SignatureConfig { + properties: { + type: { + type: number; + serializedType: number; + serializedName: "Type"; + }; + data: { + type: Buffer; + serializedType: string; + serializedName: "Data"; + }; + }; +} +declare class Signature + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + type: number; + data: Buffer; +} +declare type SerializedSignature = SerializedObject; +export { Signature, SerializedSignature }; diff --git a/src/chains/filecoin/types/src/things/start-deal-params.d.ts b/src/chains/filecoin/types/src/things/start-deal-params.d.ts new file mode 100644 index 0000000000..ee5f5ac6db --- /dev/null +++ b/src/chains/filecoin/types/src/things/start-deal-params.d.ts @@ -0,0 +1,81 @@ +import { + StorageMarketDataRef, + SerializedStorageMarketDataRef +} from "./storage-market-data-ref"; +import { Address, SerializedAddress } from "./address"; +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +declare type StartDealParamsConfig = { + properties: { + data: { + type: StorageMarketDataRef; + serializedType: SerializedStorageMarketDataRef; + serializedName: "Data"; + }; + wallet: { + type: Address | null; + serializedType: SerializedAddress | null; + serializedName: "Wallet"; + }; + miner: { + type: string; + serializedType: string; + serializedName: "Miner"; + }; + epochPrice: { + type: bigint; + serializedType: string; + serializedName: "EpochPrice"; + }; + minBlocksDuration: { + type: number; + serializedType: number; + serializedName: "MinBlocksDuration"; + }; + providerCollateral: { + type: bigint; + serializedType: string; + serializedName: "ProviderCollateral"; + }; + dealStartEpoch: { + type: number; + serializedType: number; + serializedName: "dealStartEpoch"; + }; + fastRetrieval: { + type: boolean; + serializedType: boolean; + serializedName: "FastRetrieval"; + }; + verifiedDeal: { + type: boolean; + serializedType: boolean; + serializedName: "VerifiedDeal"; + }; + }; +}; +declare class StartDealParams + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + data: StorageMarketDataRef; + wallet: Address | null; + miner: string; + epochPrice: bigint; + minBlocksDuration: number; + providerCollateral: bigint; + dealStartEpoch: number; + fastRetrieval: boolean; + verifiedDeal: boolean; +} +declare type SerializedStartDealParams = SerializedObject; +export { StartDealParams, SerializedStartDealParams }; diff --git a/src/chains/filecoin/types/src/things/storage-market-data-ref.d.ts b/src/chains/filecoin/types/src/things/storage-market-data-ref.d.ts new file mode 100644 index 0000000000..cdb76eb95f --- /dev/null +++ b/src/chains/filecoin/types/src/things/storage-market-data-ref.d.ts @@ -0,0 +1,47 @@ +import { RootCID, SerializedRootCID } from "./root-cid"; +import { + SerializableObject, + SerializedObject, + DeserializedObject, + Definitions +} from "./serializable-object"; +declare type StorageMarketDataRefConfig = { + properties: { + transferType: { + type: "graphsync"; + serializedType: "graphsync"; + serializedName: "TransferType"; + }; + root: { + type: RootCID; + serializedType: SerializedRootCID; + serializedName: "Root"; + }; + pieceCid: { + type: RootCID | null; + serializedType: SerializedRootCID | null; + serializedName: "PieceCid"; + }; + pieceSize: { + type: 0; + serializedType: 0; + serializedName: "PieceSize"; + }; + }; +}; +declare class StorageMarketDataRef + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + transferType: "graphsync"; + root: RootCID; + pieceCid: RootCID | null; + pieceSize: 0; +} +declare type SerializedStorageMarketDataRef = SerializedObject; +export { StorageMarketDataRef, SerializedStorageMarketDataRef }; diff --git a/src/chains/filecoin/filecoin/src/things/storage-proposal-data.ts b/src/chains/filecoin/types/src/things/storage-proposal-data.d.ts similarity index 59% rename from src/chains/filecoin/filecoin/src/things/storage-proposal-data.ts rename to src/chains/filecoin/types/src/things/storage-proposal-data.d.ts index 84561d3768..df36bde1ae 100644 --- a/src/chains/filecoin/filecoin/src/things/storage-proposal-data.ts +++ b/src/chains/filecoin/types/src/things/storage-proposal-data.d.ts @@ -5,8 +5,7 @@ import { DeserializedObject, Definitions } from "./serializable-object"; - -type StorageProposalDataConfig = { +declare type StorageProposalDataConfig = { properties: { transferType: { type: "graphsync"; @@ -30,39 +29,14 @@ type StorageProposalDataConfig = { }; }; }; - -class StorageProposalData +declare class StorageProposalData extends SerializableObject implements DeserializedObject { - get config(): Definitions { - return { - transferType: { - serializedName: "TransferType", - defaultValue: "graphsync" - }, - root: { - serializedName: "Root", - defaultValue: options => new RootCID(options) - }, - pieceCid: { - serializedName: "PieceCid", - defaultValue: null - }, - pieceSize: { - serializedName: "PieceSize", - defaultValue: 0 - } - }; - } - + get config(): Definitions; transferType: "graphsync"; root: RootCID; pieceCid: null; pieceSize: 0; } - -type SerializedStorageProposalData = SerializedObject< - StorageProposalDataConfig ->; - +declare type SerializedStorageProposalData = SerializedObject; export { StorageProposalData, SerializedStorageProposalData }; diff --git a/src/chains/filecoin/filecoin/src/things/storage-proposal.ts b/src/chains/filecoin/types/src/things/storage-proposal.d.ts similarity index 65% rename from src/chains/filecoin/filecoin/src/things/storage-proposal.ts rename to src/chains/filecoin/types/src/things/storage-proposal.d.ts index a98ce66104..19a21d8296 100644 --- a/src/chains/filecoin/filecoin/src/things/storage-proposal.ts +++ b/src/chains/filecoin/types/src/things/storage-proposal.d.ts @@ -10,8 +10,7 @@ import { Definitions, SerializedObject } from "./serializable-object"; - -type StorageProposalConfig = { +declare type StorageProposalConfig = { properties: { data: { type: StorageProposalData; @@ -40,40 +39,15 @@ type StorageProposalConfig = { }; }; }; - -class StorageProposal +declare class StorageProposal extends SerializableObject implements DeserializedObject { - get config(): Definitions { - return { - data: { - serializedName: "Data", - defaultValue: options => new StorageProposalData(options) - }, - wallet: { - serializedName: "Wallet" - }, - miner: { - serializedName: "Miner" - }, - epochPrice: { - serializedName: "EpochPrice", - defaultValue: "2500" - }, - minBlocksDuration: { - serializedName: "MinBlocksDuration", - defaultValue: 300 - } - }; - } - + get config(): Definitions; data: StorageProposalData; wallet: Address; miner: Miner; epochPrice: string; minBlocksDuration: number; } - -type SerializedStorageProposal = SerializedObject; - +declare type SerializedStorageProposal = SerializedObject; export { StorageProposal, SerializedStorageProposal }; diff --git a/src/chains/filecoin/types/src/things/ticket.d.ts b/src/chains/filecoin/types/src/things/ticket.d.ts new file mode 100644 index 0000000000..bffc73f4fb --- /dev/null +++ b/src/chains/filecoin/types/src/things/ticket.d.ts @@ -0,0 +1,29 @@ +/// +import { + SerializableObject, + DeserializedObject, + SerializedObject, + Definitions +} from "./serializable-object"; +interface TicketConfig { + properties: { + vrfProof: { + type: Buffer; + serializedType: string; + serializedName: "VRFProof"; + }; + }; +} +declare class Ticket + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + vrfProof: Buffer; +} +declare type SerializedTicket = SerializedObject; +export { Ticket, SerializedTicket }; diff --git a/src/chains/filecoin/types/src/things/tipset.d.ts b/src/chains/filecoin/types/src/things/tipset.d.ts new file mode 100644 index 0000000000..c2fbbec033 --- /dev/null +++ b/src/chains/filecoin/types/src/things/tipset.d.ts @@ -0,0 +1,48 @@ +import { BlockHeader, SerializedBlockHeader } from "./block-header"; +import { + SerializableObject, + DeserializedObject, + Definitions, + SerializedObject +} from "./serializable-object"; +import { RootCID, SerializedRootCID } from "./root-cid"; +interface TipsetConfig { + properties: { + cids: { + type: Array; + serializedType: Array; + serializedName: "Cids"; + }; + blocks: { + type: Array; + serializedType: Array; + serializedName: "Blocks"; + }; + height: { + type: number; + serializedType: number; + serializedName: "Height"; + }; + }; +} +declare class Tipset + extends SerializableObject + implements DeserializedObject { + get config(): Definitions; + constructor( + options?: + | Partial> + | Partial> + ); + /** + * An array that contains the BlockHeader.cid(). + * If not provided, constructor will auto add this array. + * There's no documentation specifying this, so here is + * the reference Implementation: https://git.io/Jt3VM + */ + cids: Array; + blocks: Array; + height: number; +} +declare type SerializedTipset = SerializedObject; +export { Tipset, SerializedTipset }; diff --git a/src/chains/filecoin/filecoin/src/things/win-post-proof.ts b/src/chains/filecoin/types/src/things/win-post-proof.d.ts similarity index 61% rename from src/chains/filecoin/filecoin/src/things/win-post-proof.ts rename to src/chains/filecoin/types/src/things/win-post-proof.d.ts index 4916e8d601..4720c2f675 100644 --- a/src/chains/filecoin/filecoin/src/things/win-post-proof.ts +++ b/src/chains/filecoin/types/src/things/win-post-proof.d.ts @@ -4,7 +4,6 @@ import { SerializedObject, Definitions } from "./serializable-object"; - interface WinPoStProofConfig { properties: { postProof: { @@ -19,27 +18,12 @@ interface WinPoStProofConfig { }; }; } - -class WinPoStProof +declare class WinPoStProof extends SerializableObject implements DeserializedObject { - get config(): Definitions { - return { - postProof: { - serializedName: "PoStProof", - defaultValue: 0 - }, - proofBytes: { - serializedName: "ProofBytes", - defaultValue: "dmFsaWQgcHJvb2Y=" - } - }; - } - + get config(): Definitions; postProof: number; proofBytes: string; } - -type SerializedWinPoStProof = SerializedObject; - +declare type SerializedWinPoStProof = SerializedObject; export { WinPoStProof, SerializedWinPoStProof }; diff --git a/src/chains/filecoin/types/src/types/storage-deal-status.d.ts b/src/chains/filecoin/types/src/types/storage-deal-status.d.ts new file mode 100644 index 0000000000..3017c0ad6c --- /dev/null +++ b/src/chains/filecoin/types/src/types/storage-deal-status.d.ts @@ -0,0 +1,37 @@ +export declare enum StorageDealStatus { + Unknown = 0, + ProposalNotFound = 1, + ProposalRejected = 2, + ProposalAccepted = 3, + Staged = 4, + Sealing = 5, + Finalizing = 6, + Active = 7, + Expired = 8, + Slashed = 9, + Rejecting = 10, + Failing = 11, + FundsReserved = 12, + CheckForAcceptance = 13, + Validating = 14, + AcceptWait = 15, + StartDataTransfer = 16, + Transferring = 17, + WaitingForData = 18, + VerifyData = 19, + ReserveProviderFunds = 20, + ReserveClientFunds = 21, + ProviderFunding = 22, + ClientFunding = 23, + Publish = 24, + Publishing = 25, + Error = 26, + ProviderTransferAwaitRestart = 27, + ClientTransferRestart = 28, + AwaitingPreCommit = 29 +} +export declare let terminalStates: Array; +export declare let nextSuccessfulState: Record< + StorageDealStatus, + StorageDealStatus +>; diff --git a/src/chains/filecoin/types/src/types/subscriptions.d.ts b/src/chains/filecoin/types/src/types/subscriptions.d.ts new file mode 100644 index 0000000000..9ae7726b86 --- /dev/null +++ b/src/chains/filecoin/types/src/types/subscriptions.d.ts @@ -0,0 +1,6 @@ +export declare enum SubscriptionMethod { + ChannelUpdated = "xrpc.ch.val", + ChannelClosed = "xrpc.ch.close", + SubscriptionCanceled = "xrpc.cancel" +} +export declare type SubscriptionId = string; diff --git a/src/chains/filecoin/types/tsconfig.json b/src/chains/filecoin/types/tsconfig.json new file mode 100644 index 0000000000..b0aeb919c8 --- /dev/null +++ b/src/chains/filecoin/types/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig-base.json", + "compilerOptions": { + "outDir": "lib", + "composite": true + } +} diff --git a/src/packages/cli/package.json b/src/packages/cli/package.json index 2945a38efb..30136f9824 100644 --- a/src/packages/cli/package.json +++ b/src/packages/cli/package.json @@ -49,12 +49,12 @@ "truffle" ], "devDependencies": { - "@ganache/core": "0.1.0" + "@ganache/core": "0.1.0", + "@ganache/filecoin-types": "0.1.0" }, "dependencies": { "@ganache/colors": "^0.1.0", "@ganache/ethereum": "0.1.0", - "@ganache/ethereum-options": "0.1.0", "@ganache/flavors": "0.1.0", "@ganache/options": "0.1.0", "@ganache/utils": "0.1.0", diff --git a/src/packages/cli/src/args.ts b/src/packages/cli/src/args.ts index d4a79f6efd..c1c72bd0e3 100644 --- a/src/packages/cli/src/args.ts +++ b/src/packages/cli/src/args.ts @@ -1,6 +1,10 @@ import { TruffleColors } from "@ganache/colors"; import yargs, { Options } from "yargs"; -import { DefaultFlavor, DefaultOptionsByName } from "@ganache/flavors"; +import { + DefaultFlavor, + FilecoinFlavorName, + DefaultOptionsByName +} from "@ganache/flavors"; import { Base, Definitions, @@ -48,7 +52,8 @@ function processOption( group: string, option: string, optionObj: Definitions[string | number], - argv: yargs.Argv + argv: yargs.Argv, + flavor: string ) { if (optionObj.disableInCLI !== true) { const shortHand = []; @@ -68,7 +73,7 @@ function processOption( const generateDefaultDescription = () => { // default sometimes requires a config, so we supply one return (state[option] = optionObj.default - ? optionObj.default(state).toString() + ? optionObj.default(state, flavor).toString() : undefined); }; const defaultDescription = @@ -126,6 +131,10 @@ export default function (version: string, isDocker: boolean) { let defaultPort: number; switch (flavor) { // since "ethereum" is the DefaultFlavor we don't need a `case` for it + case FilecoinFlavorName: + command = flavor; + defaultPort = 7777; + break; case DefaultFlavor: command = ["$0", flavor]; defaultPort = 8545; @@ -157,7 +166,8 @@ export default function (version: string, isDocker: boolean) { group, option, optionObj, - flavorArgs + flavorArgs, + flavor ); } } @@ -168,7 +178,15 @@ export default function (version: string, isDocker: boolean) { for (const option in categoryObj) { const optionObj = categoryObj[option]; - processOption(state, "server", group, option, optionObj, flavorArgs); + processOption( + state, + "server", + group, + option, + optionObj, + flavorArgs, + flavor + ); } flavorArgs = flavorArgs diff --git a/src/packages/cli/src/cli.ts b/src/packages/cli/src/cli.ts index deeb2e1f50..afb4909039 100644 --- a/src/packages/cli/src/cli.ts +++ b/src/packages/cli/src/cli.ts @@ -4,12 +4,11 @@ import Readline from "readline"; import Ganache, { Status } from "@ganache/core"; import { $INLINE_JSON } from "ts-transformer-inline-file"; import args from "./args"; -import { - DefaultFlavor, - FlavorName, - EthereumFlavorName -} from "@ganache/flavors"; +import { EthereumFlavorName, FilecoinFlavorName } from "@ganache/flavors"; import initializeEthereum from "./initialize/ethereum"; +import initializeFilecoin from "./initialize/filecoin"; +import { Provider as FilecoinProvider } from "@ganache/filecoin-types"; +import { Provider as EthereumProvider } from "@ganache/ethereum"; const logAndForceExit = (messages: any[], exitCode = 0) => { // https://nodejs.org/api/process.html#process_process_exit_code @@ -123,9 +122,13 @@ async function startGanache(err: Error) { started = true; switch (flavor) { + case FilecoinFlavorName: { + initializeFilecoin(server.provider as FilecoinProvider, cliSettings); + break; + } case EthereumFlavorName: default: { - initializeEthereum(server.provider, cliSettings); + initializeEthereum(server.provider as EthereumProvider, cliSettings); break; } } diff --git a/src/packages/cli/src/initialize/ethereum.ts b/src/packages/cli/src/initialize/ethereum.ts index 9997cf70fa..a3e4867b32 100644 --- a/src/packages/cli/src/initialize/ethereum.ts +++ b/src/packages/cli/src/initialize/ethereum.ts @@ -99,5 +99,5 @@ export default function (provider: Provider, cliSettings: CliSettings) { console.log(liveOptions.chain.chainId); console.log(""); - console.log("Listening on " + cliSettings.host + ":" + cliSettings.port); + console.log("RPC Listening on " + cliSettings.host + ":" + cliSettings.port); } diff --git a/src/packages/cli/src/initialize/filecoin.ts b/src/packages/cli/src/initialize/filecoin.ts new file mode 100644 index 0000000000..987d00fb43 --- /dev/null +++ b/src/packages/cli/src/initialize/filecoin.ts @@ -0,0 +1,45 @@ +import { Provider } from "@ganache/filecoin-types"; + +export default function ( + provider: Provider, + serverSettings: { host: string; port: number } +) { + const liveOptions = provider.getOptions(); + const accounts = provider.getInitialAccounts(); + + console.log(""); + console.log("Available Accounts"); + console.log("=================="); + + const addresses = Object.keys(accounts); + const attoFILinFIL = 1000000000000000000n; + + addresses.forEach(function (address, index) { + const balance = accounts[address].balance; + const strBalance = balance / attoFILinFIL; + const about = balance % attoFILinFIL === 0n ? "" : "~"; + let line = `(${index}) ${address} (${about}${strBalance} FIL)`; + + if (!accounts[address].unlocked) { + line += " 🔒"; + } + + console.log(line); + }); + + console.log(""); + console.log("Private Keys"); + console.log("=================="); + + addresses.forEach(function (address, index) { + console.log(`(${index}) ${accounts[address].secretKey}`); + }); + + console.log(""); + console.log( + `Lotus RPC listening on ${serverSettings.host}:${serverSettings.port}` + ); + console.log( + `IPFS RPC listening on ${liveOptions.chain.ipfsHost}:${liveOptions.chain.ipfsPort}` + ); +} diff --git a/src/packages/cli/tsconfig.json b/src/packages/cli/tsconfig.json index 9faebd6a95..73f6c0dc63 100644 --- a/src/packages/cli/tsconfig.json +++ b/src/packages/cli/tsconfig.json @@ -10,10 +10,6 @@ "name": "@ganache/core", "path": "../core" }, - { - "name": "@ganache/ethereum-options", - "path": "../../chains/ethereum/options" - }, { "name": "@ganache/flavors", "path": "../flavors" @@ -33,6 +29,10 @@ { "name": "@ganache/colors", "path": "../colors" + }, + { + "name": "@ganache/filecoin-types", + "path": "../../chains/filecoin/types" } ] } diff --git a/src/packages/core/src/connector-loader.ts b/src/packages/core/src/connector-loader.ts index 587a40b6bc..ad5df0dd33 100644 --- a/src/packages/core/src/connector-loader.ts +++ b/src/packages/core/src/connector-loader.ts @@ -1,9 +1,5 @@ import { utils } from "@ganache/utils"; -import { - ConnectorsByName, - DefaultFlavor, - DefaultOptionsByName -} from "@ganache/flavors"; +import { DefaultFlavor, GetConnector } from "@ganache/flavors"; import { Options as ProviderOptions } from "@ganache/flavors"; import { hasOwn } from "@ganache/utils/src/utils"; import { Base, Definitions } from "@ganache/options"; @@ -25,7 +21,7 @@ export default { // for that (yet), instead of "all" (0) or just 1 as we are doing here: const asyncRequestProcessing = "chain" in providerOptions - ? providerOptions.chain.asyncRequestProcessing + ? providerOptions.chain!.asyncRequestProcessing : (providerOptions as any).asyncRequestProcessing; const requestCoordinator = new utils.RequestCoordinator( asyncRequestProcessing ? 0 : 1 @@ -36,10 +32,11 @@ export default { // to a RequestCoordinator. const executor = new utils.Executor(requestCoordinator); - const connector = new ConnectorsByName[flavor]( + const connector = GetConnector( + flavor, providerOptions as any, executor - ); + ) as any; // The request coordinator is initialized in a "paused" state; when the provider is ready we unpause. // This lets us accept queue requests before we've even fully initialized. diff --git a/src/packages/core/src/options/server-options.ts b/src/packages/core/src/options/server-options.ts index e6d7aca8d9..4f250fd1c9 100644 --- a/src/packages/core/src/options/server-options.ts +++ b/src/packages/core/src/options/server-options.ts @@ -1,3 +1,4 @@ +import { DefaultFlavor, FilecoinFlavorName } from "@ganache/flavors"; import { Definitions } from "@ganache/options"; export type ServerConfig = { @@ -31,6 +32,16 @@ export type ServerConfig = { type: boolean | "auto"; hasDefault: true; }; + + /** + * Defines the endpoint route the HTTP and WebSocket servers will listen on. + * + * @default "/" + */ + readonly rpcEndpoint: { + type: string; + hasDefault: true; + }; }; }; const normalize = (rawInput: T) => rawInput; @@ -49,5 +60,20 @@ export const ServerOptions: Definitions = { "Whether or not websockets should response with binary data (ArrayBuffers) or strings.", default: () => "auto", cliChoices: [true, false, "auto"] as any[] + }, + rpcEndpoint: { + normalize, + cliDescription: + "Defines the endpoint route the HTTP and WebSocket servers will listen on.", + default: (config, flavor) => { + switch (flavor) { + case FilecoinFlavorName: + return "/rpc/v0"; + case DefaultFlavor: + default: + return "/"; + } + }, + defaultDescription: '"/" (Ethereum), "/rpc/v0" (Filecoin)' } }; diff --git a/src/packages/core/src/server.ts b/src/packages/core/src/server.ts index bc877353a0..2ac3a21179 100644 --- a/src/packages/core/src/server.ts +++ b/src/packages/core/src/server.ts @@ -74,7 +74,7 @@ export default class Server { opts.server ); } - this.#httpServer = new HttpServer(_app, connector); + this.#httpServer = new HttpServer(_app, connector, opts.server); } listen(port: number): Promise; diff --git a/src/packages/core/src/servers/http-server.ts b/src/packages/core/src/servers/http-server.ts index 23b349add2..61325b20cc 100644 --- a/src/packages/core/src/servers/http-server.ts +++ b/src/packages/core/src/servers/http-server.ts @@ -7,6 +7,7 @@ import { import ContentTypes from "./utils/content-types"; import HttpResponseCodes from "./utils/http-response-codes"; import { Connector } from "@ganache/flavors"; +import { InternalOptions } from "../options"; type HttpMethods = "GET" | "OPTIONS" | "POST"; @@ -84,13 +85,21 @@ function sendResponse( }); } +export type HttpServerOptions = Pick; + export default class HttpServer { #connector: Connector; - constructor(app: TemplatedApp, connector: Connector) { + constructor( + app: TemplatedApp, + connector: Connector, + options: HttpServerOptions + ) { this.#connector = connector; // JSON-RPC routes... - app.post("/", this.#handlePost).options("/", this.#handleOptions); + app + .post(options.rpcEndpoint, this.#handlePost) + .options(options.rpcEndpoint, this.#handleOptions); // because Easter Eggs are fun... app.get("/418", response => { @@ -139,8 +148,12 @@ export default class HttpServer { response.onData((message: ArrayBuffer, isLast: boolean) => { const chunk = Buffer.from(message); if (isLast) { - const connector = this.#connector; - let payload: ReturnType; + // we have to use any here because typescript isn't smart enough + // to understand the ambiguity of RequestFormat and ReturnType + // on the Connector interface must match up appropriately + const connector = this.#connector as any; + + let payload: ReturnType; try { const message = buffer ? Buffer.concat([buffer, chunk]) : chunk; payload = connector.parse(message); diff --git a/src/packages/core/src/servers/ws-server.ts b/src/packages/core/src/servers/ws-server.ts index 792da2d0e3..8e3ddc8b28 100644 --- a/src/packages/core/src/servers/ws-server.ts +++ b/src/packages/core/src/servers/ws-server.ts @@ -1,6 +1,6 @@ import uWS, { TemplatedApp, WebSocket } from "uWebSockets.js"; import WebSocketCloseCodes from "./utils/websocket-close-codes"; -import { ServerOptions } from "../options"; +import { InternalOptions } from "../options"; import * as Flavors from "@ganache/flavors"; import { PromiEvent } from "@ganache/utils"; @@ -20,7 +20,10 @@ export type WebSocketCapableFlavor = { export type GanacheWebSocket = WebSocket & { closed?: boolean }; -export type WebsocketServerOptions = Pick; +export type WebsocketServerOptions = Pick< + InternalOptions["server"], + "wsBinary" | "rpcEndpoint" +>; export default class WebsocketServer { #connections = new Map void>>(); @@ -32,7 +35,7 @@ export default class WebsocketServer { const connections = this.#connections; const wsBinary = options.wsBinary; const autoBinary = wsBinary === "auto"; - app.ws("/", { + app.ws(options.rpcEndpoint, { /* WS Options */ compression: uWS.SHARED_COMPRESSOR, // Zero memory overhead compression maxPayloadLength: 16 * 1024, // 128 Kibibits @@ -49,7 +52,10 @@ export default class WebsocketServer { message: ArrayBuffer, isBinary: boolean ) => { - let payload: ReturnType; + // We have to use type any instead of ReturnType + // on `payload` because Typescript isn't smart enough to understand the + // ambiguity doesn't actually exist + let payload: any; const useBinary = autoBinary ? isBinary : (wsBinary as boolean); try { payload = connector.parse(Buffer.from(message)); diff --git a/src/packages/core/tests/connector.test.ts b/src/packages/core/tests/connector.test.ts index 4828110351..17471c1d8c 100644 --- a/src/packages/core/tests/connector.test.ts +++ b/src/packages/core/tests/connector.test.ts @@ -9,7 +9,9 @@ describe("connector", () => { it("it logs when `options.verbose` is `true`", async () => { const logger = { log: (_msg: string) => {} }; - const p = Ganache.provider({ logging: { logger, verbose: true } }); + const p = Ganache.provider({ + logging: { logger, verbose: true } + }) as EthereumProvider; logger.log = msg => { assert.strictEqual( @@ -32,7 +34,7 @@ describe("connector", () => { }); it("it processes requests asyncronously when `asyncRequestProcessing` is default (true)", async () => { - const p = Ganache.provider(); + const p = Ganache.provider() as EthereumProvider; const accounts = await p.send("eth_accounts"); // `eth_accounts` should always be faster than eth_getBalance; eth_accounts // should return before eth_getBalance because of the @@ -45,7 +47,9 @@ describe("connector", () => { }); it("it processes requests syncronously when `asyncRequestProcessing` is `false`", async () => { - const p = Ganache.provider({ chain: { asyncRequestProcessing: false } }); + const p = Ganache.provider({ + chain: { asyncRequestProcessing: false } + }) as EthereumProvider; const accounts = await p.send("eth_accounts"); // eth_getBalance should return first even though eth_accounts is faster; // eth_getBalance should return before eth_accounts because of the @@ -60,7 +64,7 @@ describe("connector", () => { // to make sure that 3rd party API implementations can't shoot themselves // in the foot on accident it.skip("TODO: allow 'injecting' our own engine or API into a provider!", async () => { - const p = Ganache.provider(); + const p = Ganache.provider() as EthereumProvider; // this won't work becase ganache uses _real_ private properties that can't // be duck punched. This test is supposed to ensure that _real_ non-function // own properties (and __proto__ properties) can't be executed. @@ -71,7 +75,7 @@ describe("connector", () => { }); it("rejects invalid rpc methods", async () => { - const p = Ganache.provider(); + const p = Ganache.provider() as EthereumProvider; const illegalMethodNames = [ "toString", diff --git a/src/packages/core/tests/server.test.ts b/src/packages/core/tests/server.test.ts index 50897ef5de..c48cf0caaf 100644 --- a/src/packages/core/tests/server.test.ts +++ b/src/packages/core/tests/server.test.ts @@ -9,6 +9,7 @@ import intoStream = require("into-stream"); import { PromiEvent } from "@ganache/utils"; import { promisify } from "util"; import { ServerOptions } from "../src/options"; +import { Provider as EthereumProvider } from "@ganache/ethereum"; const IS_WINDOWS = process.platform === "win32"; @@ -36,6 +37,9 @@ describe("server", () => { } } ) { + // @ts-ignore - `s` errors if you run tsc and then test + // because it tries to compare the built declaration file to + // the TS file, causing missing # private variables s = Ganache.server(options); return s.listen(port); } @@ -106,6 +110,9 @@ describe("server", () => { }); it("returns the net_version over a legacy-style connection listener", done => { + // @ts-ignore - `s` errors if you run tsc and then test + // because it tries to compare the built declaration file to + // the TS file, causing missing # private variables s = Ganache.server({ chain: { networkId } }); @@ -189,6 +196,9 @@ describe("server", () => { server.listen(port); try { + // @ts-ignore - `s` errors if you run tsc and then test + // because it tries to compare the built declaration file to + // the TS file, causing missing # private variables const s = Ganache.server(); const listen = promisify(s.listen.bind(s)); await assert.rejects(listen(port), { @@ -205,6 +215,9 @@ describe("server", () => { "fails to listen if the socket is already in use by Ganache", async () => { await setup(); + // @ts-ignore - `s` errors if you run tsc and then test + // because it tries to compare the built declaration file to + // the TS file, causing missing # private variables const s2 = Ganache.server(); try { @@ -429,7 +442,7 @@ describe("server", () => { await setup(); try { - const provider = s.provider; + const provider = s.provider as EthereumProvider; const oldRequestRaw = (provider as any)._requestRaw; const req = request.post("http://localhost:" + port); const abortPromise = new Promise(resolve => { @@ -651,7 +664,7 @@ describe("server", () => { }); it("doesn't crash when the connection is closed while a request is in flight", async () => { - const provider = s.provider; + const provider = s.provider as EthereumProvider; provider._requestRaw = (async () => { // close our websocket after intercepting the request await s.close(); @@ -680,7 +693,7 @@ describe("server", () => { }); it("handles PromiEvent messages", async () => { - const provider = s.provider; + const provider = s.provider as EthereumProvider; const message = "I hope you get this message"; const oldRequestRaw = provider._requestRaw.bind(provider); provider._requestRaw = (async () => { @@ -787,7 +800,7 @@ describe("server", () => { }); it("doesn't crash when the connection is closed while a subscription is in flight", async () => { - const provider = s.provider; + const provider = s.provider as EthereumProvider; let promiEvent: PromiEvent; provider._requestRaw = (async () => { promiEvent = new PromiEvent(resolve => { @@ -835,7 +848,7 @@ describe("server", () => { // create tons of data to force websocket backpressure const huge = {}; for (let i = 0; i < 1e6; i++) huge["prop_" + i] = { i }; - s.provider._requestRaw = (async () => { + (s.provider as EthereumProvider)._requestRaw = (async () => { return { value: Promise.resolve(huge) }; }) as any; } diff --git a/src/packages/flavors/index.ts b/src/packages/flavors/index.ts index 122d712368..75ace831a8 100644 --- a/src/packages/flavors/index.ts +++ b/src/packages/flavors/index.ts @@ -1,23 +1,33 @@ import * as Ethereum from "@ganache/ethereum"; -import { ethereumDefaults } from "@ganache/ethereum-options"; +import { + EthereumDefaults, + EthereumProviderOptions, + EthereumLegacyProviderOptions +} from "@ganache/ethereum-options"; + +import * as Filecoin from "@ganache/filecoin-types"; +import { + FilecoinDefaults, + FilecoinProviderOptions, + FilecoinLegacyProviderOptions +} from "@ganache/filecoin-options"; // we need "@ganache/options" in order for TS to properly infer types for `DefaultOptionsByName` import "@ganache/options"; export const EthereumFlavorName = "ethereum"; +export const FilecoinFlavorName = "filecoin"; export const DefaultFlavor = EthereumFlavorName; export const DefaultOptionsByName = { - [EthereumFlavorName]: ethereumDefaults + [EthereumFlavorName]: EthereumDefaults, + [FilecoinFlavorName]: FilecoinDefaults }; export type ConnectorsByName = { [EthereumFlavorName]: Ethereum.Connector; -}; - -export const ConnectorsByName = { - [EthereumFlavorName]: Ethereum.Connector + [FilecoinFlavorName]: Filecoin.Connector; }; export type FlavorName = keyof ConnectorsByName; @@ -26,10 +36,40 @@ export type Connector = { [K in keyof ConnectorsByName]: ConnectorsByName[K]; }[keyof ConnectorsByName]; -export type Providers = Ethereum.Provider; +export function GetConnector( + flavor: FlavorName, + providerOptions: any, + executor +): Connector { + switch (flavor) { + case DefaultFlavor: + return new Ethereum.Connector(providerOptions, executor); + case FilecoinFlavorName: + try { + const connector: Filecoin.Connector = require("@ganache/filecoin") + .Connector; + // @ts-ignore + return new connector(providerOptions, executor); + } catch (e) { + if (e.message.includes("Cannot find module '@ganache/filecoin'")) { + throw new Error( + "Could not find module @ganache/filecoin peer dependency; please run `npm install @ganache/filecoin`" + ); + } else { + throw e; + } + } + } +} + +export type Providers = Ethereum.Provider | Filecoin.Provider; type EthereumOptions = { flavor?: typeof EthereumFlavorName; -} & Ethereum.ProviderOptions; +} & (EthereumProviderOptions | EthereumLegacyProviderOptions); + +type FilecoinOptions = { + flavor?: typeof FilecoinFlavorName; +} & (FilecoinProviderOptions | FilecoinLegacyProviderOptions); -export type Options = EthereumOptions; +export type Options = EthereumOptions | FilecoinOptions; diff --git a/src/packages/flavors/package.json b/src/packages/flavors/package.json index 62351c8e6c..5654b3d626 100644 --- a/src/packages/flavors/package.json +++ b/src/packages/flavors/package.json @@ -43,8 +43,16 @@ "dependencies": { "@ganache/ethereum": "^0.1.0", "@ganache/ethereum-options": "0.1.0", + "@ganache/filecoin-options": "0.1.0", "@ganache/options": "0.1.0", "@ganache/tezos": "^0.1.0", - "@ganache/utils": "^0.1.0" + "@ganache/utils": "0.1.0" + }, + "devDependencies": { + "@ganache/filecoin": "0.1.0", + "@ganache/filecoin-types": "0.1.0" + }, + "peerDependencies": { + "@ganache/filecoin": "0.1.0" } } diff --git a/src/packages/flavors/tsconfig.json b/src/packages/flavors/tsconfig.json index bef9b17cdd..65a573c777 100644 --- a/src/packages/flavors/tsconfig.json +++ b/src/packages/flavors/tsconfig.json @@ -22,9 +22,21 @@ "name": "@ganache/ethereum-options", "path": "../../chains/ethereum/options" }, + { + "name": "@ganache/filecoin", + "path": "../../chains/filecoin/filecoin" + }, + { + "name": "@ganache/filecoin-options", + "path": "../../chains/filecoin/options" + }, { "name": "@ganache/options", "path": "../options" + }, + { + "name": "@ganache/filecoin-types", + "path": "../../chains/filecoin/types" } ] } diff --git a/src/packages/options/src/create.ts b/src/packages/options/src/create.ts index 7b3fb0bdd3..8cd2c4d9f2 100644 --- a/src/packages/options/src/create.ts +++ b/src/packages/options/src/create.ts @@ -43,6 +43,7 @@ const checkForConflicts = ( function fill(defaults: any, options: any, target: any, namespace: any) { const def = defaults[namespace]; const config = (target[namespace] = target[namespace] || {}); + const flavor = options.flavor; const suppliedOptions = new Set(); const keys = Object.keys(def); @@ -77,7 +78,7 @@ function fill(defaults: any, options: any, target: any, namespace: any) { config[key] = normalized; suppliedOptions.add(key); } else if (hasOwn(propDefinition, "default")) { - config[key] = propDefinition.default(config); + config[key] = propDefinition.default(config, flavor); } } } @@ -99,7 +100,7 @@ function fill(defaults: any, options: any, target: any, namespace: any) { config[key] = normalized; suppliedOptions.add(key); } else if (hasOwn(propDefinition, "default")) { - config[key] = propDefinition.default(config); + config[key] = propDefinition.default(config, flavor); } } } diff --git a/src/packages/options/src/definition.ts b/src/packages/options/src/definition.ts index d5aec5f94d..d594661fce 100644 --- a/src/packages/options/src/definition.ts +++ b/src/packages/options/src/definition.ts @@ -57,7 +57,11 @@ export type Definitions = { (void extends OptionHasDefault ? {} : { - readonly default: (config: InternalConfig) => OptionType; + // using type string for flavor to prevent circular dependency + readonly default: ( + config: InternalConfig, + flavor: string + ) => OptionType; readonly defaultDescription?: string; }) & // hasLegacy diff --git a/src/packages/utils/index.ts b/src/packages/utils/index.ts index 236ca0179c..c2580d60a2 100644 --- a/src/packages/utils/index.ts +++ b/src/packages/utils/index.ts @@ -1,6 +1,7 @@ export * as types from "./src/types"; export * as utils from "./src/utils"; export * as JsonRpc from "./src/things/json-rpc"; +export * from "./src/things/subscription"; export * from "./src/things/json-rpc/json-rpc-quantity"; export * from "./src/things/json-rpc/json-rpc-data"; export { default as JsonRpcTypes } from "./src/things/jsonrpc"; diff --git a/src/packages/utils/npm-shrinkwrap.json b/src/packages/utils/npm-shrinkwrap.json index fb3be24d7e..c4e57458b2 100644 --- a/src/packages/utils/npm-shrinkwrap.json +++ b/src/packages/utils/npm-shrinkwrap.json @@ -52,6 +52,11 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==" }, + "seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "uWebSockets.js": { "version": "github:uNetworking/uWebSockets.js#3dbec7b56d627193e20705844b6bd10e49848b8c", "from": "github:uNetworking/uWebSockets.js#v18.4.0", diff --git a/src/packages/utils/package.json b/src/packages/utils/package.json index 3f328fd766..06b60dad3e 100644 --- a/src/packages/utils/package.json +++ b/src/packages/utils/package.json @@ -45,7 +45,8 @@ ], "dependencies": { "emittery": "0.7.2", - "keccak": "3.0.1" + "keccak": "3.0.1", + "seedrandom": "3.0.5" }, "devDependencies": { "uWebSockets.js": "github:uNetworking/uWebSockets.js#v18.4.0" diff --git a/src/packages/utils/src/things/jsonrpc.ts b/src/packages/utils/src/things/jsonrpc.ts index 41185fd18d..da855aa9b5 100644 --- a/src/packages/utils/src/things/jsonrpc.ts +++ b/src/packages/utils/src/things/jsonrpc.ts @@ -41,7 +41,7 @@ namespace JsonRpc { }; }; export const Error = ( - id: string, + id: string | undefined, error: T, result?: unknown ): Error => { diff --git a/src/packages/utils/src/things/subscription.ts b/src/packages/utils/src/things/subscription.ts new file mode 100644 index 0000000000..f1868643f7 --- /dev/null +++ b/src/packages/utils/src/things/subscription.ts @@ -0,0 +1,6 @@ +import Quantity from "./json-rpc/json-rpc-quantity"; + +export interface Subscription { + id: Quantity; + unsubscribe: () => void; +} diff --git a/src/packages/utils/src/utils/index.ts b/src/packages/utils/src/utils/index.ts index 62b5d75c06..dd99286151 100644 --- a/src/packages/utils/src/utils/index.ts +++ b/src/packages/utils/src/utils/index.ts @@ -9,3 +9,4 @@ export * from "./constants"; export * from "./buffer-to-key"; export * from "./keccak"; export * from "./find-insert-position"; +export * from "./random-number-generator"; diff --git a/src/packages/utils/src/utils/random-number-generator.ts b/src/packages/utils/src/utils/random-number-generator.ts new file mode 100644 index 0000000000..4f334677c5 --- /dev/null +++ b/src/packages/utils/src/utils/random-number-generator.ts @@ -0,0 +1,37 @@ +import seedrandom from "seedrandom"; + +export class RandomNumberGenerator { + readonly rng: () => number; + + constructor(seed?: string) { + if (seed) { + this.rng = seedrandom.alea(seed); + } else { + this.rng = Math.random; + } + } + + getNumber(upperExclusiveBound: number = 1): number { + // I believe this check may be a tiny bit faster than + // always multiplying by 1 + if (upperExclusiveBound !== 1) { + return this.rng() * upperExclusiveBound; + } else { + return this.rng(); + } + } + + getNumbers(length: number, upperExclusiveBound: number = 1): number[] { + const numbers: number[] = []; + + for (let i = 0; i < length; i++) { + numbers.push(this.getNumber(upperExclusiveBound)); + } + + return numbers; + } + + getBuffer(length: number): Buffer { + return Buffer.from(this.getNumbers(length, 256)); + } +}