diff --git a/package-lock.json b/package-lock.json index 3b45139301c..9b533a29113 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1392,6 +1392,16 @@ "@types/node": "*" } }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, "node_modules/@types/chalk": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-2.2.0.tgz", @@ -1503,6 +1513,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "node_modules/@types/jwt-simple": { + "version": "0.5.33", + "resolved": "https://registry.npmjs.org/@types/jwt-simple/-/jwt-simple-0.5.33.tgz", + "integrity": "sha1-+4Ocq+gUN5VPfQzQF2CtgJbsUm4=", + "dev": true + }, "node_modules/@types/k-bucket": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/k-bucket/-/k-bucket-5.0.1.tgz", @@ -3121,20 +3137,19 @@ "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" }, "node_modules/body-parser": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", - "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", - "dev": true, + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", "dependencies": { - "bytes": "3.1.1", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", "http-errors": "1.8.1", "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.9.6", - "raw-body": "2.4.2", + "qs": "6.9.7", + "raw-body": "2.4.3", "type-is": "~1.6.18" }, "engines": { @@ -3145,7 +3160,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "dependencies": { "ms": "2.0.0" } @@ -3153,8 +3167,7 @@ "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -3504,10 +3517,9 @@ } }, "node_modules/bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", - "dev": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { "node": ">= 0.8" } @@ -4117,7 +4129,6 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, "dependencies": { "debug": "2.6.9", "finalhandler": "1.1.2", @@ -4132,7 +4143,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "dependencies": { "ms": "2.0.0" } @@ -4140,8 +4150,7 @@ "node_modules/connect/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "node_modules/console-browserify": { "version": "1.2.0", @@ -4186,7 +4195,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -4889,7 +4897,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true, "engines": { "node": ">= 0.6" } @@ -5212,8 +5219,7 @@ "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "node_modules/electron-fetch": { "version": "1.7.4", @@ -5274,7 +5280,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true, "engines": { "node": ">= 0.8" } @@ -5628,8 +5633,7 @@ "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "node_modules/escape-string-regexp": { "version": "1.0.5", @@ -6794,7 +6798,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -6812,7 +6815,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "dependencies": { "ms": "2.0.0" } @@ -6820,8 +6822,7 @@ "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "node_modules/find-cache-dir": { "version": "3.3.2", @@ -7632,7 +7633,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dev": true, "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", @@ -7741,7 +7741,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -9707,6 +9706,14 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/jwt-simple": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/jwt-simple/-/jwt-simple-0.5.6.tgz", + "integrity": "sha512-40aUybvhH9t2h71ncA1/1SbtTNCVZHgsTsTgqPUxGWDmUDrXyDf2wMNQKEbdBjbf4AI+fQhbECNTV6lWxQKUzg==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/k-bucket": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/k-bucket/-/k-bucket-5.1.0.tgz", @@ -11828,7 +11835,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true, "engines": { "node": ">= 0.6" } @@ -13071,7 +13077,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, "dependencies": { "ee-first": "1.1.1" }, @@ -13532,7 +13537,6 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, "engines": { "node": ">= 0.8" } @@ -14431,10 +14435,9 @@ } }, "node_modules/qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", - "dev": true, + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", "engines": { "node": ">=0.6" }, @@ -14535,12 +14538,11 @@ } }, "node_modules/raw-body": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", - "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", - "dev": true, + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", "dependencies": { - "bytes": "3.1.1", + "bytes": "3.1.2", "http-errors": "1.8.1", "iconv-lite": "0.4.24", "unpipe": "1.0.0" @@ -15332,8 +15334,7 @@ "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "node_modules/sha.js": { "version": "2.4.11", @@ -16531,7 +16532,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true, "engines": { "node": ">= 0.6" } @@ -17350,7 +17350,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, "engines": { "node": ">=0.6" } @@ -17593,7 +17592,6 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -17772,7 +17770,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true, "engines": { "node": ">= 0.8" } @@ -17875,7 +17872,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true, "engines": { "node": ">= 0.4.0" } @@ -19190,13 +19186,16 @@ "@ethereumjs/ethash": "^1.1.0", "@ethereumjs/tx": "^3.5.0", "@ethereumjs/vm": "^5.7.1", + "body-parser": "^1.19.2", "chalk": "^4.1.2", + "connect": "^3.7.0", "debug": "^4.3.3", "ethereumjs-util": "^7.1.4", "fs-extra": "^10.0.0", "it-pipe": "^1.1.0", "it-pushable": "^1.4.2", "jayson": "^3.4.4", + "jwt-simple": "^0.5.6", "level": "^6.0.0", "level-mem": "^5.0.1", "libp2p": "^0.30.7", @@ -19219,7 +19218,10 @@ }, "devDependencies": { "@babel/plugin-transform-spread": "^7.10.1", + "@types/body-parser": "^1.19.2", + "@types/connect": "^3.4.35", "@types/fs-extra": "^8.1.0", + "@types/jwt-simple": "^0.5.33", "@types/levelup": "^4.3.0", "@types/node": "^16.11.7", "@types/tape": "^4.13.2", @@ -20753,11 +20755,16 @@ "@ethereumjs/ethash": "^1.1.0", "@ethereumjs/tx": "^3.5.0", "@ethereumjs/vm": "^5.7.1", + "@types/body-parser": "^1.19.2", + "@types/connect": "^3.4.35", "@types/fs-extra": "^8.1.0", + "@types/jwt-simple": "^0.5.33", "@types/levelup": "^4.3.0", "@types/node": "^16.11.7", "@types/tape": "^4.13.2", + "body-parser": "^1.19.2", "chalk": "^4.1.2", + "connect": "^3.7.0", "constants-browserify": "^1.0.0", "crypto-browserify": "^3.12.0", "debug": "^4.3.3", @@ -20769,6 +20776,7 @@ "it-pipe": "^1.1.0", "it-pushable": "^1.4.2", "jayson": "^3.4.4", + "jwt-simple": "^0.5.6", "karma": "^6.3.2", "karma-chrome-launcher": "^3.1.0", "karma-firefox-launcher": "^2.1.0", @@ -22008,6 +22016,16 @@ "@types/node": "*" } }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, "@types/chalk": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-2.2.0.tgz", @@ -22118,6 +22136,12 @@ "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, + "@types/jwt-simple": { + "version": "0.5.33", + "resolved": "https://registry.npmjs.org/@types/jwt-simple/-/jwt-simple-0.5.33.tgz", + "integrity": "sha1-+4Ocq+gUN5VPfQzQF2CtgJbsUm4=", + "dev": true + }, "@types/k-bucket": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@types/k-bucket/-/k-bucket-5.0.1.tgz", @@ -23424,20 +23448,19 @@ "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" }, "body-parser": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", - "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", - "dev": true, + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", "requires": { - "bytes": "3.1.1", + "bytes": "3.1.2", "content-type": "~1.0.4", "debug": "2.6.9", "depd": "~1.1.2", "http-errors": "1.8.1", "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.9.6", - "raw-body": "2.4.2", + "qs": "6.9.7", + "raw-body": "2.4.3", "type-is": "~1.6.18" }, "dependencies": { @@ -23445,7 +23468,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -23453,8 +23475,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -23779,10 +23800,9 @@ } }, "bytes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", - "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==", - "dev": true + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, "cached-path-relative": { "version": "1.1.0", @@ -24292,7 +24312,6 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, "requires": { "debug": "2.6.9", "finalhandler": "1.1.2", @@ -24304,7 +24323,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -24312,8 +24330,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -24355,8 +24372,7 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "convert-source-map": { "version": "1.8.0", @@ -24982,8 +24998,7 @@ "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" }, "deps-sort": { "version": "2.0.1", @@ -25280,8 +25295,7 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-fetch": { "version": "1.7.4", @@ -25337,8 +25351,7 @@ "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, "encoding": { "version": "0.1.13", @@ -25639,8 +25652,7 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "escape-string-regexp": { "version": "1.0.5", @@ -26601,7 +26613,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -26616,7 +26627,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -26624,8 +26634,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -27223,7 +27232,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dev": true, "requires": { "depd": "~1.1.2", "inherits": "2.0.4", @@ -27307,7 +27315,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -28794,6 +28801,11 @@ "safe-buffer": "^5.0.1" } }, + "jwt-simple": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/jwt-simple/-/jwt-simple-0.5.6.tgz", + "integrity": "sha512-40aUybvhH9t2h71ncA1/1SbtTNCVZHgsTsTgqPUxGWDmUDrXyDf2wMNQKEbdBjbf4AI+fQhbECNTV6lWxQKUzg==" + }, "k-bucket": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/k-bucket/-/k-bucket-5.1.0.tgz", @@ -30577,8 +30589,7 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "memdown": { "version": "5.1.0", @@ -31678,7 +31689,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, "requires": { "ee-first": "1.1.1" } @@ -32014,8 +32024,7 @@ "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, "path-browserify": { "version": "0.0.1", @@ -32747,10 +32756,9 @@ "dev": true }, "qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", - "dev": true + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" }, "querystring": { "version": "0.2.0", @@ -32817,12 +32825,11 @@ "dev": true }, "raw-body": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", - "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", - "dev": true, + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", "requires": { - "bytes": "3.1.1", + "bytes": "3.1.2", "http-errors": "1.8.1", "iconv-lite": "0.4.24", "unpipe": "1.0.0" @@ -33523,8 +33530,7 @@ "setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, "sha.js": { "version": "2.4.11", @@ -34491,8 +34497,7 @@ "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, "stealthy-require": { "version": "1.1.1", @@ -35151,8 +35156,7 @@ "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "tough-cookie": { "version": "2.5.0", @@ -35321,7 +35325,6 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, "requires": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -35446,8 +35449,7 @@ "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "upper-case": { "version": "1.1.3", @@ -35542,8 +35544,7 @@ "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { "version": "8.3.2", diff --git a/packages/client/.eslintrc.js b/packages/client/.eslintrc.js index 6924095ec87..cc645e4c256 100644 --- a/packages/client/.eslintrc.js +++ b/packages/client/.eslintrc.js @@ -1,26 +1,22 @@ module.exports = { - extends: "../../config/eslint.js", + extends: '../../config/eslint.js', parserOptions: { - project: ['./tsconfig.json', './tsconfig.browser.json', './tsconfig.eslint.json'] - }, - rules: { + project: ['./tsconfig.json', './tsconfig.browser.json', './tsconfig.eslint.json'], }, + rules: {}, overrides: [ { files: ['test/**/*.ts'], rules: { - 'implicit-dependencies/no-implicit': [ - 'error', - { peer: false, dev: true, optional: false }, - ], + 'implicit-dependencies/no-implicit': ['error', { peer: false, dev: true, optional: false }], }, }, { - files: ['bin/cli.ts'], + files: ['bin/**.ts'], rules: { - 'no-console': 'off' + 'no-console': 'off', }, }, ], - ignorePatterns: ['webpack.config.js'] + ignorePatterns: ['webpack.config.js'], } diff --git a/packages/client/bin/cli.ts b/packages/client/bin/cli.ts index ddc17d3c219..e43de8eab07 100755 --- a/packages/client/bin/cli.ts +++ b/packages/client/bin/cli.ts @@ -5,16 +5,14 @@ import path from 'path' import readline from 'readline' import { randomBytes } from 'crypto' import { ensureDirSync, readFileSync, removeSync } from 'fs-extra' -import { Server as RPCServer } from 'jayson/promise' import Common, { Chain, Hardfork } from '@ethereumjs/common' import { _getInitializedChains } from '@ethereumjs/common/dist/chains' import { Address, toBuffer } from 'ethereumjs-util' -import { parseMultiaddrs, parseGenesisState, parseCustomParams, inspectParams } from '../lib/util' +import { parseMultiaddrs, parseGenesisState, parseCustomParams } from '../lib/util' import EthereumClient from '../lib/client' import { Config, DataDirectory } from '../lib/config' import { Logger, getLogger } from '../lib/logging' -import { RPCManager } from '../lib/rpc' -import * as modules from '../lib/rpc/modules' +import { startRPCServers, helprpc } from './startRpc' import type { Chain as IChain, GenesisState } from '@ethereumjs/common/dist/types' const level = require('level') const yargs = require('yargs/yargs') @@ -122,6 +120,10 @@ const args = yargs(hideBin(process.argv)) string: true, default: 'localhost', }) + .option('jwt-secret', { + describe: 'Provide a file containing a hex encoded jwt secret for Engine RPC server', + coerce: (arg: string) => (arg ? path.resolve(arg) : undefined), + }) .option('helprpc', { describe: 'Display the JSON RPC help with a list of all RPC methods implemented (and exit)', boolean: true, @@ -309,105 +311,6 @@ async function startClient(config: Config) { return client } -/** - * Starts and returns enabled RPCServers - */ -function startRPCServers(client: EthereumClient) { - const config = client.config - const onRequest = (request: any) => { - let msg = '' - if (args.rpcDebug) { - msg += `${request.method} called with params:\n${inspectParams(request.params)}` - } else { - msg += `${request.method} called with params: ${inspectParams(request.params, 125)}` - } - config.logger.debug(msg) - } - - const handleResponse = (request: any, response: any, batchAddOn = '') => { - let msg = '' - if (args.rpcDebug) { - msg = `${request.method}${batchAddOn} responded with:\n${inspectParams(response)}` - } else { - msg = `${request.method}${batchAddOn} responded with: ` - if (response.result) { - msg += inspectParams(response, 125) - } - if (response.error) { - msg += `error: ${response.error.message}` - } - } - config.logger.debug(msg) - } - - const onBatchResponse = (request: any, response: any) => { - // Batch request - if (request.length !== undefined) { - if (response.length === undefined || response.length !== request.length) { - config.logger.debug('Invalid batch request received.') - return - } - for (let i = 0; i < request.length; i++) { - handleResponse(request[i], response[i], ' (batch request)') - } - } else { - handleResponse(request, response) - } - } - - const servers: RPCServer[] = [] - const { rpc, rpcaddr, rpcport, ws, wsPort, wsAddr, rpcEngine, rpcEngineAddr, rpcEnginePort } = - args - const manager = new RPCManager(client, config) - - if (rpc || ws) { - const methods = - rpcEngine && rpcEnginePort === rpcport && rpcEngineAddr === rpcaddr - ? { ...manager.getMethods(), ...manager.getMethods(true) } - : { ...manager.getMethods() } - const server = new RPCServer(methods) - server.on('request', onRequest) - server.on('response', onBatchResponse) - const namespaces = [...new Set(Object.keys(methods).map((m) => m.split('_')[0]))].join(',') - if (rpc) { - server.http().listen(rpcport) - config.logger.info( - `Started JSON RPC Server address=http://${rpcaddr}:${rpcport} namespaces=${namespaces}` - ) - } - if (ws) { - const opts: any = { port: wsPort } - if (rpcaddr === wsAddr && rpcport === wsPort) { - // If http and ws are listening on the same port, - // pass in the existing server to prevent a listening error - delete opts.port - opts.server = server - } - server.websocket(opts) - config.logger.info( - `Started JSON RPC Server address=ws://${wsAddr}:${wsPort} namespaces=${namespaces}` - ) - } - servers.push(server) - } - - if (rpcEngine) { - if (rpc && rpcport === rpcEnginePort && rpcaddr === rpcEngineAddr) { - return servers - } - const server = new RPCServer(manager.getMethods(true)) - config.logger.info( - `Started JSON RPC server address=http://${rpcEngineAddr}:${rpcEnginePort} namespaces=engine` - ) - server.http().listen(rpcEnginePort) - server.on('request', onRequest) - server.on('response', onBatchResponse) - servers.push(server) - } - - return servers -} - /** * Returns a configured common for devnet with a prefunded address */ @@ -522,26 +425,6 @@ async function inputAccounts() { return accounts } -/** - * Output RPC help and exit - */ -function helprpc() { - console.log('-'.repeat(27)) - console.log('JSON-RPC: Supported Methods') - console.log('-'.repeat(27)) - console.log() - for (const modName of modules.list) { - console.log(`${modName}:`) - const methods = RPCManager.getMethodNames((modules as any)[modName]) - for (const methodName of methods) { - console.log(`-> ${modName.toLowerCase()}_${methodName}`) - } - console.log() - } - console.log() - process.exit() -} - /** * Returns a randomly generated account */ @@ -669,7 +552,7 @@ async function run() { config.events.setMaxListeners(50) const client = await startClient(config) - const servers = args.rpc || args.rpcEngine ? startRPCServers(client) : [] + const servers = args.rpc || args.rpcEngine ? startRPCServers(client, args) : [] process.on('SIGINT', async () => { config.logger.info('Caught interrupt signal. Shutting down...') diff --git a/packages/client/bin/startRpc.ts b/packages/client/bin/startRpc.ts new file mode 100644 index 00000000000..18266ba5a69 --- /dev/null +++ b/packages/client/bin/startRpc.ts @@ -0,0 +1,199 @@ +import { Server as RPCServer } from 'jayson/promise' +import { readFileSync, writeFileSync } from 'fs-extra' +import { RPCManager } from '../lib/rpc' +import EthereumClient from '../lib/client' +import { inspectParams, createRPCServerListener, createWsRPCServerListener } from '../lib/util' +import * as modules from '../lib/rpc/modules' +import { Config } from '../lib/config' + +type RPCArgs = { + rpc: boolean + rpcaddr: string + rpcport: number + ws: boolean + wsPort: number + wsAddr: string + rpcEngine: boolean + rpcEngineAddr: string + rpcEnginePort: number + rpcDebug: boolean + helprpc: boolean + 'jwt-secret'?: string +} + +/** + * Returns a jwt secret from a provided file path, otherwise saves a randomly generated one to datadir + */ +function parseJwtSecret(config: Config, jwtFilePath?: string): Buffer { + let jwtSecret + if (jwtFilePath) { + const jwtSecretContents = readFileSync(jwtFilePath, 'utf-8').trim() + const hexPattern = new RegExp(/^(0x|0X)?(?[a-fA-F0-9]+)$/, 'g') + const jwtSecretHex = hexPattern.exec(jwtSecretContents)?.groups?.jwtSecret + if (!jwtSecretHex || jwtSecretHex.length != 64) { + throw Error('Need a valid 256 bit hex encoded secret') + } + config.logger.debug(`Read a hex encoded secret, path=${jwtFilePath}`) + jwtSecret = Buffer.from(jwtSecretHex, 'hex') + } else { + jwtFilePath = `${config.datadir}/jwtsecret` + jwtSecret = Buffer.from(Array.from({ length: 32 }, () => Math.round(Math.random() * 255))) + writeFileSync(jwtFilePath, jwtSecret.toString('hex')) + config.logger.info(`A hex encoded random jwt secret written, path=${jwtFilePath}`) + } + return jwtSecret +} + +/** + * Starts and returns enabled RPCServers + */ +export function startRPCServers(client: EthereumClient, args: RPCArgs) { + const config = client.config + const onRequest = (request: any) => { + let msg = '' + if (args.rpcDebug) { + msg += `${request.method} called with params:\n${inspectParams(request.params)}` + } else { + msg += `${request.method} called with params: ${inspectParams(request.params, 125)}` + } + config.logger.debug(msg) + } + + const handleResponse = (request: any, response: any, batchAddOn = '') => { + let msg = '' + if (args.rpcDebug) { + msg = `${request.method}${batchAddOn} responded with:\n${inspectParams(response)}` + } else { + msg = `${request.method}${batchAddOn} responded with: ` + if (response.result) { + msg += inspectParams(response, 125) + } + if (response.error) { + msg += `error: ${response.error.message}` + } + } + config.logger.debug(msg) + } + + const onBatchResponse = (request: any, response: any) => { + // Batch request + if (request.length !== undefined) { + if (response.length === undefined || response.length !== request.length) { + config.logger.debug('Invalid batch request received.') + return + } + for (let i = 0; i < request.length; i++) { + handleResponse(request[i], response[i], ' (batch request)') + } + } else { + handleResponse(request, response) + } + } + + const servers: RPCServer[] = [] + const { + rpc, + rpcaddr, + rpcport, + ws, + wsPort, + wsAddr, + rpcEngine, + rpcEngineAddr, + rpcEnginePort, + 'jwt-secret': jwtSecretPath, + } = args + const manager = new RPCManager(client, config) + const jwtSecret = parseJwtSecret(config, jwtSecretPath) + + if (rpc || ws) { + const withEngineMethods = rpcEngine && rpcEnginePort === rpcport && rpcEngineAddr === rpcaddr + const methods = withEngineMethods + ? { ...manager.getMethods(), ...manager.getMethods(true) } + : { ...manager.getMethods() } + const server = new RPCServer(methods) + server.on('request', onRequest) + server.on('response', onBatchResponse) + const namespaces = [...new Set(Object.keys(methods).map((m) => m.split('_')[0]))].join(',') + let rpcHttpServer + + if (rpc) { + rpcHttpServer = createRPCServerListener({ + server, + withEngineMiddleware: withEngineMethods + ? { + jwtSecret, + unlessFn: (req: any) => + Array.isArray(req.body) + ? !req.body.some((r: any) => r.method.includes('engine_')) + : !req.body.method.includes('engine_'), + } + : undefined, + }) + rpcHttpServer.listen(rpcport) + config.logger.info( + `Started JSON RPC Server address=http://${rpcaddr}:${rpcport} namespaces=${namespaces}` + ) + } + if (ws) { + const opts: any = { + server, + withEngineMiddleware: withEngineMethods ? { jwtSecret } : undefined, + } + if (rpcaddr === wsAddr && rpcport === wsPort) { + // We want to loadon the websocket upgrade request to the same server + Object.assign(opts, { httpServer: rpcHttpServer }) + } + + const rpcWsServer = createWsRPCServerListener(opts) + if (rpcWsServer) rpcWsServer.listen(wsPort) + config.logger.info( + `Started JSON RPC Server address=ws://${wsAddr}:${wsPort} namespaces=${namespaces}` + ) + } + servers.push(server) + } + + if (rpcEngine) { + if (rpc && rpcport === rpcEnginePort && rpcaddr === rpcEngineAddr) { + return servers + } + const server = new RPCServer(manager.getMethods(true)) + server.on('request', onRequest) + server.on('response', onBatchResponse) + + createRPCServerListener({ + server, + withEngineMiddleware: { + jwtSecret, + }, + }).listen(rpcport) + config.logger.info( + `Started JSON RPC server address=http://${rpcEngineAddr}:${rpcEnginePort} namespaces=engine` + ) + + servers.push(server) + } + + return servers +} + +/** + * Output RPC help and exit + */ +export function helprpc() { + console.log('-'.repeat(27)) + console.log('JSON-RPC: Supported Methods') + console.log('-'.repeat(27)) + console.log() + for (const modName of modules.list) { + console.log(`${modName}:`) + const methods = RPCManager.getMethodNames((modules as any)[modName]) + for (const methodName of methods) { + console.log(`-> ${modName.toLowerCase()}_${methodName}`) + } + console.log() + } + console.log() + process.exit() +} diff --git a/packages/client/lib/util/index.ts b/packages/client/lib/util/index.ts index defa9d9726f..c10ae90b0db 100644 --- a/packages/client/lib/util/index.ts +++ b/packages/client/lib/util/index.ts @@ -6,6 +6,7 @@ import { inspect } from 'util' import { version as packageVersion } from '../../package.json' export * from './parse' +export * from './rpc' export function short(buffer: Buffer): string { return buffer.toString('hex').slice(0, 8) + '...' diff --git a/packages/client/lib/util/rpc.ts b/packages/client/lib/util/rpc.ts new file mode 100644 index 00000000000..44297ce0ab0 --- /dev/null +++ b/packages/client/lib/util/rpc.ts @@ -0,0 +1,80 @@ +import { createServer } from 'http' +import { Server as RPCServer, HttpServer } from 'jayson/promise' +import { json as jsonParser } from 'body-parser' +import { decode, TAlgorithm } from 'jwt-simple' +import Connect, { IncomingMessage } from 'connect' + +const algorithm: TAlgorithm = 'HS256' + +type CreateRPCServerListenerOpts = { + server: RPCServer + withEngineMiddleware?: WithEngineMiddleware +} +type WithEngineMiddleware = { jwtSecret: Buffer; unlessFn?: (req: IncomingMessage) => boolean } + +function checkHeaderAuth(req: any, jwtSecret: Buffer): void { + const header = (req.headers['Authorization'] ?? req.headers['authorization']) as string + if (!header) throw Error(`Missing auth header`) + const token = header.trim().split(' ')[1] + if (!token) throw Error(`Missing jwt token`) + const claims = decode(token.trim(), jwtSecret as never as string, false, algorithm) + if (Math.abs(new Date().getTime() - claims.iat * 1000 ?? 0) > 5000) { + throw Error('Stale jwt token') + } +} + +export function createRPCServerListener(opts: CreateRPCServerListenerOpts): HttpServer { + const { server, withEngineMiddleware } = opts + const app = Connect() + app.use(jsonParser()) + + if (withEngineMiddleware) { + const { jwtSecret, unlessFn } = withEngineMiddleware + app.use(function (req, res, next) { + try { + if (unlessFn) { + if (unlessFn(req)) return next() + } + checkHeaderAuth(req, jwtSecret) + return next() + } catch (error) { + if (error instanceof Error) { + res.writeHead(401) + res.end(`Unauthorized: ${error}`) + } else next(error) + } + }) + } + + app.use(server.middleware()) + const httpServer = createServer(app) + return httpServer +} + +export function createWsRPCServerListener( + opts: CreateRPCServerListenerOpts & { httpServer?: HttpServer } +): HttpServer | undefined { + const { server, withEngineMiddleware } = opts + // Get the server to hookup upgrade request on + const httpServer = opts.httpServer ?? createServer() + const wss = server.websocket({ noServer: true }) + + httpServer.on('upgrade', (req, socket, head) => { + if (withEngineMiddleware) { + const { jwtSecret } = withEngineMiddleware + try { + checkHeaderAuth(req, jwtSecret) + } catch (error) { + socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n') + socket.destroy() + } + } + // @ts-ignore + wss.handleUpgrade(req, socket, head, function done(ws) { + // @ts-ignore + wss.emit('connection', ws, req) + }) + }) + // Only return something if a new server was created + return !opts.httpServer ? httpServer : undefined +} diff --git a/packages/client/package.json b/packages/client/package.json index efd9867fe26..dda49ad1fa1 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -57,13 +57,16 @@ "@ethereumjs/ethash": "^1.1.0", "@ethereumjs/tx": "^3.5.0", "@ethereumjs/vm": "^5.7.1", + "body-parser": "^1.19.2", "chalk": "^4.1.2", + "connect": "^3.7.0", "debug": "^4.3.3", "ethereumjs-util": "^7.1.4", "fs-extra": "^10.0.0", "it-pipe": "^1.1.0", "it-pushable": "^1.4.2", "jayson": "^3.4.4", + "jwt-simple": "^0.5.6", "level": "^6.0.0", "level-mem": "^5.0.1", "libp2p": "^0.30.7", @@ -83,7 +86,10 @@ }, "devDependencies": { "@babel/plugin-transform-spread": "^7.10.1", + "@types/body-parser": "^1.19.2", + "@types/connect": "^3.4.35", "@types/fs-extra": "^8.1.0", + "@types/jwt-simple": "^0.5.33", "@types/levelup": "^4.3.0", "@types/node": "^16.11.7", "@types/tape": "^4.13.2", diff --git a/packages/client/test/rpc/helpers.ts b/packages/client/test/rpc/helpers.ts index b896c9bb77a..c3251744096 100644 --- a/packages/client/test/rpc/helpers.ts +++ b/packages/client/test/rpc/helpers.ts @@ -11,18 +11,26 @@ import { parseCustomParams, parseGenesisState } from '../../lib/util' import { TxPool } from '../../lib/sync/txpool' import { RlpxServer } from '../../lib/net/server/rlpxserver' import { VMExecution } from '../../lib/execution' +import { createRPCServerListener } from '../../lib/util' import { mockBlockchain } from './mockBlockchain' -import type EthereumClient from '../../lib/client' +import type { IncomingMessage } from 'connect' import type { TypedTransaction } from '@ethereumjs/tx' +import type EthereumClient from '../../lib/client' const request = require('supertest') const level = require('level-mem') const config: any = {} config.logger = getLogger(config) -export function startRPC(methods: any, port: number = 3000) { +type WithEngineMiddleware = { jwtSecret: Buffer; unlessFn?: (req: IncomingMessage) => boolean } + +export function startRPC( + methods: any, + port: number = 3000, + withEngineMiddleware?: WithEngineMiddleware +) { const server = new RPCServer(methods) - const httpServer = server.http() + const httpServer = createRPCServerListener({ server, withEngineMiddleware }) httpServer.listen(port) return httpServer } diff --git a/packages/client/test/rpc/rpc.spec.ts b/packages/client/test/rpc/rpc.spec.ts index ae0188a5199..9896e5412c0 100644 --- a/packages/client/test/rpc/rpc.spec.ts +++ b/packages/client/test/rpc/rpc.spec.ts @@ -1,16 +1,81 @@ import tape from 'tape' -const request = require('supertest') +import { encode, TAlgorithm } from 'jwt-simple' import { startRPC, closeRPC } from './helpers' import { METHOD_NOT_FOUND } from '../../lib/rpc/error-code' +const request = require('supertest') + +const jwtSecret = Buffer.from(Array.from({ length: 32 }, () => Math.round(Math.random() * 255))) tape('call JSON-RPC without Content-Type header', (t) => { const server = startRPC({}) + const req = 'plaintext' + + request(server) + .post('/') + .send(req) + .expect(415) + .end((err: any) => { + closeRPC(server) + t.end(err) + }) +}) +tape('call JSON-RPC auth protected server without any auth headers', (t) => { + const server = startRPC({}, undefined, { jwtSecret }) const req = 'plaintext' request(server) .post('/') .send(req) + .expect(401) + .end((err: any) => { + closeRPC(server) + t.end(err) + }) +}) + +tape('call JSON-RPC auth protected server with invalid token', (t) => { + const server = startRPC({}, undefined, { jwtSecret }) + const req = 'plaintext' + + request(server) + .post('/') + .set('Authorization', 'Bearer invalidtoken') + .send(req) + .expect(401) + .end((err: any) => { + closeRPC(server) + t.end(err) + }) +}) + +tape('call JSON-RPC auth protected server with an invalid algorithm token', (t) => { + const server = startRPC({}, undefined, { jwtSecret }) + const req = 'plaintext' + const claims = { iat: Math.floor(new Date().getTime() / 1000) } + const token = encode(claims, jwtSecret as never as string, 'HS512' as TAlgorithm) + + request(server) + .post('/') + .set('Authorization', `Bearer ${token}`) + .send(req) + .expect(401) + .end((err: any) => { + closeRPC(server) + t.end(err) + }) +}) + +tape('call JSON-RPC auth protected server with a valid token', (t) => { + const server = startRPC({}, undefined, { jwtSecret }) + const req = 'plaintext' + const claims = { iat: Math.floor(new Date().getTime() / 1000) } + const token = encode(claims, jwtSecret as never as string, 'HS256' as TAlgorithm) + + request(server) + .post('/') + .set('Authorization', `Bearer ${token}`) + .send(req) .expect(415) .end((err: any) => { closeRPC(server) @@ -18,6 +83,37 @@ tape('call JSON-RPC without Content-Type header', (t) => { }) }) +tape('call JSON-RPC auth protected server with a valid but stale token', (t) => { + const server = startRPC({}, undefined, { jwtSecret }) + const req = 'plaintext' + const claims = { iat: Math.floor(new Date().getTime() / 1000 - 6) } + const token = encode(claims, jwtSecret as never as string, 'HS256' as TAlgorithm) + + request(server) + .post('/') + .set('Authorization', `Bearer ${token}`) + .send(req) + .expect(401) + .end((err: any) => { + closeRPC(server) + t.end(err) + }) +}) + +tape('call JSON-RPC without Content-Type header', (t) => { + const server = startRPC({}, undefined, { jwtSecret }) + const req = 'plaintext' + + request(server) + .post('/') + .send(req) + .expect(401) + .end((err: any) => { + closeRPC(server) + t.end(err) + }) +}) + tape('call JSON RPC with non-exist method', (t) => { const server = startRPC({}) const req = { @@ -44,3 +140,78 @@ tape('call JSON RPC with non-exist method', (t) => { t.end(err) }) }) + +tape('call JSON-RPC auth protected server with unprotected method without token ', (t) => { + const server = startRPC({}, undefined, { + jwtSecret, + unlessFn: (req: any) => req.body.method.includes('unprotected_'), + }) + + const req = { + jsonrpc: '2.0', + method: 'unprotected_METHOD_DOES_NOT_EXIST', + params: ['0x1', true], + id: 1, + } + + request(server) + .post('/') + .set('Content-Type', 'application/json') + .send(req) + .expect(200) + .end((err: any) => { + closeRPC(server) + t.end(err) + }) +}) + +tape('call JSON-RPC auth protected server with protected method without token ', (t) => { + const server = startRPC({}, undefined, { + jwtSecret, + unlessFn: (req: any) => !req.body.method.includes('protected_'), + }) + + const req = { + jsonrpc: '2.0', + method: 'protected_METHOD_DOES_NOT_EXIST', + params: ['0x1', true], + id: 1, + } + + request(server) + .post('/') + .set('Content-Type', 'application/json') + .send(req) + .expect(401) + .end((err: any) => { + closeRPC(server) + t.end(err) + }) +}) + +tape('call JSON-RPC auth protected server with protected method with token ', (t) => { + const server = startRPC({}, undefined, { + jwtSecret, + unlessFn: (req: any) => !req.body.method.includes('protected_'), + }) + + const req = { + jsonrpc: '2.0', + method: 'protected_METHOD_DOES_NOT_EXIST', + params: ['0x1', true], + id: 1, + } + const claims = { iat: Math.floor(new Date().getTime() / 1000) } + const token = encode(claims, jwtSecret as never as string, 'HS256' as TAlgorithm) + + request(server) + .post('/') + .set('Content-Type', 'application/json') + .set('Authorization', `Bearer ${token}`) + .send(req) + .expect(200) + .end((err: any) => { + closeRPC(server) + t.end(err) + }) +}) diff --git a/packages/client/webpack.config.js b/packages/client/webpack.config.js index b782838b006..a91f27af8cc 100644 --- a/packages/client/webpack.config.js +++ b/packages/client/webpack.config.js @@ -46,14 +46,16 @@ module.exports = { resolve: { fallback: { buffer: require.resolve('buffer'), - constants: require.resolve("constants-browserify"), + constants: require.resolve('constants-browserify'), crypto: require.resolve('crypto-browserify'), // used by: rlpxpeer, bin/cli.ts dgram: false, // used by: rlpxpeer via @ethereumjs/devp2p + http: false, // used by: jayson fs: false, // used by: FullSynchronizer via @ethereumjs/vm net: false, // used by: rlpxpeer os: require.resolve('os-browserify/browser'), // used by: bin/cli.ts, web3_clientVersion rpc path: false, // used by: bin/cli.ts stream: require.resolve('stream-browserify'), // used by: fetcher + zlib: false, // used by: body-parser }, }, performance: {