├── README.md └── nodejs └── part1 ├── README.md ├── package-lock.json ├── package.json ├── src ├── api.js ├── block.js ├── blockchain.js ├── consensus.js ├── server.js └── utils.js └── test ├── block.js ├── blockchain.js └── consensus.js /README.md: -------------------------------------------------------------------------------- 1 | # kauri-learn-to-build-a-blockchain 2 | 3 | Source code for Kauri series of articles on [Creating a Blockchain](https://kauri.io/article/92034a0c23ed4cb4a6ca959e0a4b78b9/) 4 | 5 | ## Contents 6 | 7 | 1. Part 1: [Blocks & Consensus](/nodejs/part1) 8 | 1. Part 2: Transactions, wallets and public/private keys - coming soon 9 | 1. Part 3: Transactions with state (alternative to UTXO) 10 | 11 | ## Contributing 12 | 13 | Contributions are welcome! Please feel free to submit a PR and/or a [Kauri](https://kauri.io) article 14 | -------------------------------------------------------------------------------- /nodejs/part1/README.md: -------------------------------------------------------------------------------- 1 | # kauri-learn-to-build-a-blockchain Part1: Blocks & Consensus 2 | 3 | Source code for Kauri series of articles on [Creating a Blockchain | Blocks & Consensus](https://kauri.io/article/92034a0c23ed4cb4a6ca959e0a4b78b9/) 4 | 5 | ## Installation 6 | 7 | 1. Install [Node & NPM](https://nodejs.org) 8 | 2. Clone this respo 9 | 3. Run `npm install` 10 | 11 | ## Run 12 | 13 | Start a node 14 | 15 | ``` 16 | node src/server.js port= 17 | ``` 18 | 19 | Mine a block 20 | 21 | ``` 22 | curl -X POST "localhost:/mine" -H 'Content-Type: application/json' -d' 23 | { 24 | "data": "Mine a block on node 1" 25 | } 26 | ' 27 | ``` 28 | 29 | Add peer to node 30 | 31 | ``` 32 | curl -X POST "localhost:/peers/add" -H 'Content-Type: application/json' -d' 33 | { 34 | "peers": ["http://:"] 35 | } 36 | ' 37 | ``` 38 | 39 | ## Contributing 40 | 41 | Contributions are welcome! Please feel free to submit a PR and/or a [Kauri](https://kauri.io) article 42 | -------------------------------------------------------------------------------- /nodejs/part1/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blockchain-js", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "requires": { 12 | "mime-types": "~2.1.18", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "ansi-colors": { 17 | "version": "3.2.3", 18 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", 19 | "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==" 20 | }, 21 | "ansi-regex": { 22 | "version": "3.0.0", 23 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 24 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 25 | }, 26 | "ansi-styles": { 27 | "version": "3.2.1", 28 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 29 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 30 | "requires": { 31 | "color-convert": "^1.9.0" 32 | } 33 | }, 34 | "append-field": { 35 | "version": "1.0.0", 36 | "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", 37 | "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" 38 | }, 39 | "argparse": { 40 | "version": "1.0.10", 41 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 42 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 43 | "requires": { 44 | "sprintf-js": "~1.0.2" 45 | } 46 | }, 47 | "array-flatten": { 48 | "version": "1.1.1", 49 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 50 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 51 | }, 52 | "balanced-match": { 53 | "version": "1.0.0", 54 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 55 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 56 | }, 57 | "body-parser": { 58 | "version": "1.18.3", 59 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 60 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 61 | "requires": { 62 | "bytes": "3.0.0", 63 | "content-type": "~1.0.4", 64 | "debug": "2.6.9", 65 | "depd": "~1.1.2", 66 | "http-errors": "~1.6.3", 67 | "iconv-lite": "0.4.23", 68 | "on-finished": "~2.3.0", 69 | "qs": "6.5.2", 70 | "raw-body": "2.3.3", 71 | "type-is": "~1.6.16" 72 | } 73 | }, 74 | "brace-expansion": { 75 | "version": "1.1.11", 76 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 77 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 78 | "requires": { 79 | "balanced-match": "^1.0.0", 80 | "concat-map": "0.0.1" 81 | } 82 | }, 83 | "browser-stdout": { 84 | "version": "1.3.1", 85 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 86 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" 87 | }, 88 | "buffer-from": { 89 | "version": "1.1.1", 90 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 91 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" 92 | }, 93 | "busboy": { 94 | "version": "0.2.14", 95 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", 96 | "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", 97 | "requires": { 98 | "dicer": "0.2.5", 99 | "readable-stream": "1.1.x" 100 | } 101 | }, 102 | "bytes": { 103 | "version": "3.0.0", 104 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 105 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 106 | }, 107 | "camelcase": { 108 | "version": "5.3.1", 109 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 110 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" 111 | }, 112 | "chalk": { 113 | "version": "2.4.2", 114 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 115 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 116 | "requires": { 117 | "ansi-styles": "^3.2.1", 118 | "escape-string-regexp": "^1.0.5", 119 | "supports-color": "^5.3.0" 120 | }, 121 | "dependencies": { 122 | "supports-color": { 123 | "version": "5.5.0", 124 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 125 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 126 | "requires": { 127 | "has-flag": "^3.0.0" 128 | } 129 | } 130 | } 131 | }, 132 | "cliui": { 133 | "version": "4.1.0", 134 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", 135 | "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", 136 | "requires": { 137 | "string-width": "^2.1.1", 138 | "strip-ansi": "^4.0.0", 139 | "wrap-ansi": "^2.0.0" 140 | } 141 | }, 142 | "code-point-at": { 143 | "version": "1.1.0", 144 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 145 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 146 | }, 147 | "color-convert": { 148 | "version": "1.9.3", 149 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 150 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 151 | "requires": { 152 | "color-name": "1.1.3" 153 | } 154 | }, 155 | "color-name": { 156 | "version": "1.1.3", 157 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 158 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 159 | }, 160 | "concat-map": { 161 | "version": "0.0.1", 162 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 163 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 164 | }, 165 | "concat-stream": { 166 | "version": "1.6.2", 167 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 168 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 169 | "requires": { 170 | "buffer-from": "^1.0.0", 171 | "inherits": "^2.0.3", 172 | "readable-stream": "^2.2.2", 173 | "typedarray": "^0.0.6" 174 | }, 175 | "dependencies": { 176 | "isarray": { 177 | "version": "1.0.0", 178 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 179 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 180 | }, 181 | "readable-stream": { 182 | "version": "2.3.6", 183 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 184 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 185 | "requires": { 186 | "core-util-is": "~1.0.0", 187 | "inherits": "~2.0.3", 188 | "isarray": "~1.0.0", 189 | "process-nextick-args": "~2.0.0", 190 | "safe-buffer": "~5.1.1", 191 | "string_decoder": "~1.1.1", 192 | "util-deprecate": "~1.0.1" 193 | } 194 | }, 195 | "string_decoder": { 196 | "version": "1.1.1", 197 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 198 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 199 | "requires": { 200 | "safe-buffer": "~5.1.0" 201 | } 202 | } 203 | } 204 | }, 205 | "content-disposition": { 206 | "version": "0.5.2", 207 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 208 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 209 | }, 210 | "content-type": { 211 | "version": "1.0.4", 212 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 213 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 214 | }, 215 | "cookie": { 216 | "version": "0.3.1", 217 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 218 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 219 | }, 220 | "cookie-signature": { 221 | "version": "1.0.6", 222 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 223 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 224 | }, 225 | "core-util-is": { 226 | "version": "1.0.2", 227 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 228 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 229 | }, 230 | "cross-spawn": { 231 | "version": "6.0.5", 232 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 233 | "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 234 | "requires": { 235 | "nice-try": "^1.0.4", 236 | "path-key": "^2.0.1", 237 | "semver": "^5.5.0", 238 | "shebang-command": "^1.2.0", 239 | "which": "^1.2.9" 240 | } 241 | }, 242 | "crypto-js": { 243 | "version": "3.1.9-1", 244 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.9-1.tgz", 245 | "integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg=" 246 | }, 247 | "debug": { 248 | "version": "2.6.9", 249 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 250 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 251 | "requires": { 252 | "ms": "2.0.0" 253 | } 254 | }, 255 | "decamelize": { 256 | "version": "1.2.0", 257 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 258 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 259 | }, 260 | "define-properties": { 261 | "version": "1.1.3", 262 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 263 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 264 | "requires": { 265 | "object-keys": "^1.0.12" 266 | } 267 | }, 268 | "depd": { 269 | "version": "1.1.2", 270 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 271 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 272 | }, 273 | "destroy": { 274 | "version": "1.0.4", 275 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 276 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 277 | }, 278 | "dicer": { 279 | "version": "0.2.5", 280 | "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", 281 | "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", 282 | "requires": { 283 | "readable-stream": "1.1.x", 284 | "streamsearch": "0.1.2" 285 | } 286 | }, 287 | "diff": { 288 | "version": "3.5.0", 289 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 290 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" 291 | }, 292 | "ee-first": { 293 | "version": "1.1.1", 294 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 295 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 296 | }, 297 | "emoji-regex": { 298 | "version": "7.0.3", 299 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 300 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" 301 | }, 302 | "encodeurl": { 303 | "version": "1.0.2", 304 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 305 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 306 | }, 307 | "end-of-stream": { 308 | "version": "1.4.1", 309 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", 310 | "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", 311 | "requires": { 312 | "once": "^1.4.0" 313 | } 314 | }, 315 | "es-abstract": { 316 | "version": "1.13.0", 317 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", 318 | "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", 319 | "requires": { 320 | "es-to-primitive": "^1.2.0", 321 | "function-bind": "^1.1.1", 322 | "has": "^1.0.3", 323 | "is-callable": "^1.1.4", 324 | "is-regex": "^1.0.4", 325 | "object-keys": "^1.0.12" 326 | } 327 | }, 328 | "es-to-primitive": { 329 | "version": "1.2.0", 330 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", 331 | "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", 332 | "requires": { 333 | "is-callable": "^1.1.4", 334 | "is-date-object": "^1.0.1", 335 | "is-symbol": "^1.0.2" 336 | } 337 | }, 338 | "escape-html": { 339 | "version": "1.0.3", 340 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 341 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 342 | }, 343 | "escape-string-regexp": { 344 | "version": "1.0.5", 345 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 346 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 347 | }, 348 | "esprima": { 349 | "version": "4.0.1", 350 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 351 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" 352 | }, 353 | "etag": { 354 | "version": "1.8.1", 355 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 356 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 357 | }, 358 | "execa": { 359 | "version": "1.0.0", 360 | "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", 361 | "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", 362 | "requires": { 363 | "cross-spawn": "^6.0.0", 364 | "get-stream": "^4.0.0", 365 | "is-stream": "^1.1.0", 366 | "npm-run-path": "^2.0.0", 367 | "p-finally": "^1.0.0", 368 | "signal-exit": "^3.0.0", 369 | "strip-eof": "^1.0.0" 370 | } 371 | }, 372 | "express": { 373 | "version": "4.16.4", 374 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", 375 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", 376 | "requires": { 377 | "accepts": "~1.3.5", 378 | "array-flatten": "1.1.1", 379 | "body-parser": "1.18.3", 380 | "content-disposition": "0.5.2", 381 | "content-type": "~1.0.4", 382 | "cookie": "0.3.1", 383 | "cookie-signature": "1.0.6", 384 | "debug": "2.6.9", 385 | "depd": "~1.1.2", 386 | "encodeurl": "~1.0.2", 387 | "escape-html": "~1.0.3", 388 | "etag": "~1.8.1", 389 | "finalhandler": "1.1.1", 390 | "fresh": "0.5.2", 391 | "merge-descriptors": "1.0.1", 392 | "methods": "~1.1.2", 393 | "on-finished": "~2.3.0", 394 | "parseurl": "~1.3.2", 395 | "path-to-regexp": "0.1.7", 396 | "proxy-addr": "~2.0.4", 397 | "qs": "6.5.2", 398 | "range-parser": "~1.2.0", 399 | "safe-buffer": "5.1.2", 400 | "send": "0.16.2", 401 | "serve-static": "1.13.2", 402 | "setprototypeof": "1.1.0", 403 | "statuses": "~1.4.0", 404 | "type-is": "~1.6.16", 405 | "utils-merge": "1.0.1", 406 | "vary": "~1.1.2" 407 | } 408 | }, 409 | "finalhandler": { 410 | "version": "1.1.1", 411 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 412 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 413 | "requires": { 414 | "debug": "2.6.9", 415 | "encodeurl": "~1.0.2", 416 | "escape-html": "~1.0.3", 417 | "on-finished": "~2.3.0", 418 | "parseurl": "~1.3.2", 419 | "statuses": "~1.4.0", 420 | "unpipe": "~1.0.0" 421 | } 422 | }, 423 | "find-up": { 424 | "version": "3.0.0", 425 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 426 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 427 | "requires": { 428 | "locate-path": "^3.0.0" 429 | } 430 | }, 431 | "flat": { 432 | "version": "4.1.0", 433 | "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", 434 | "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", 435 | "requires": { 436 | "is-buffer": "~2.0.3" 437 | } 438 | }, 439 | "forwarded": { 440 | "version": "0.1.2", 441 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 442 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 443 | }, 444 | "fresh": { 445 | "version": "0.5.2", 446 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 447 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 448 | }, 449 | "fs.realpath": { 450 | "version": "1.0.0", 451 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 452 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 453 | }, 454 | "function-bind": { 455 | "version": "1.1.1", 456 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 457 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 458 | }, 459 | "get-caller-file": { 460 | "version": "2.0.5", 461 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 462 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" 463 | }, 464 | "get-stream": { 465 | "version": "4.1.0", 466 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", 467 | "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", 468 | "requires": { 469 | "pump": "^3.0.0" 470 | } 471 | }, 472 | "glob": { 473 | "version": "7.1.3", 474 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 475 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 476 | "requires": { 477 | "fs.realpath": "^1.0.0", 478 | "inflight": "^1.0.4", 479 | "inherits": "2", 480 | "minimatch": "^3.0.4", 481 | "once": "^1.3.0", 482 | "path-is-absolute": "^1.0.0" 483 | } 484 | }, 485 | "growl": { 486 | "version": "1.10.5", 487 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 488 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" 489 | }, 490 | "has": { 491 | "version": "1.0.3", 492 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 493 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 494 | "requires": { 495 | "function-bind": "^1.1.1" 496 | } 497 | }, 498 | "has-flag": { 499 | "version": "3.0.0", 500 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 501 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 502 | }, 503 | "has-symbols": { 504 | "version": "1.0.0", 505 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", 506 | "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" 507 | }, 508 | "he": { 509 | "version": "1.2.0", 510 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 511 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" 512 | }, 513 | "http-errors": { 514 | "version": "1.6.3", 515 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 516 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 517 | "requires": { 518 | "depd": "~1.1.2", 519 | "inherits": "2.0.3", 520 | "setprototypeof": "1.1.0", 521 | "statuses": ">= 1.4.0 < 2" 522 | } 523 | }, 524 | "iconv-lite": { 525 | "version": "0.4.23", 526 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 527 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 528 | "requires": { 529 | "safer-buffer": ">= 2.1.2 < 3" 530 | } 531 | }, 532 | "inflight": { 533 | "version": "1.0.6", 534 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 535 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 536 | "requires": { 537 | "once": "^1.3.0", 538 | "wrappy": "1" 539 | } 540 | }, 541 | "inherits": { 542 | "version": "2.0.3", 543 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 544 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 545 | }, 546 | "invert-kv": { 547 | "version": "2.0.0", 548 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", 549 | "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" 550 | }, 551 | "ipaddr.js": { 552 | "version": "1.8.0", 553 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", 554 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" 555 | }, 556 | "is-buffer": { 557 | "version": "2.0.3", 558 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", 559 | "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" 560 | }, 561 | "is-callable": { 562 | "version": "1.1.4", 563 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", 564 | "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" 565 | }, 566 | "is-date-object": { 567 | "version": "1.0.1", 568 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", 569 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" 570 | }, 571 | "is-fullwidth-code-point": { 572 | "version": "2.0.0", 573 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 574 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 575 | }, 576 | "is-regex": { 577 | "version": "1.0.4", 578 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", 579 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", 580 | "requires": { 581 | "has": "^1.0.1" 582 | } 583 | }, 584 | "is-stream": { 585 | "version": "1.1.0", 586 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 587 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 588 | }, 589 | "is-symbol": { 590 | "version": "1.0.2", 591 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", 592 | "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", 593 | "requires": { 594 | "has-symbols": "^1.0.0" 595 | } 596 | }, 597 | "isarray": { 598 | "version": "0.0.1", 599 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 600 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 601 | }, 602 | "isexe": { 603 | "version": "2.0.0", 604 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 605 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 606 | }, 607 | "js-yaml": { 608 | "version": "3.13.1", 609 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 610 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 611 | "requires": { 612 | "argparse": "^1.0.7", 613 | "esprima": "^4.0.0" 614 | } 615 | }, 616 | "lcid": { 617 | "version": "2.0.0", 618 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", 619 | "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", 620 | "requires": { 621 | "invert-kv": "^2.0.0" 622 | } 623 | }, 624 | "locate-path": { 625 | "version": "3.0.0", 626 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 627 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 628 | "requires": { 629 | "p-locate": "^3.0.0", 630 | "path-exists": "^3.0.0" 631 | } 632 | }, 633 | "lodash": { 634 | "version": "4.17.11", 635 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 636 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" 637 | }, 638 | "log-symbols": { 639 | "version": "2.2.0", 640 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", 641 | "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", 642 | "requires": { 643 | "chalk": "^2.0.1" 644 | } 645 | }, 646 | "map-age-cleaner": { 647 | "version": "0.1.3", 648 | "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", 649 | "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", 650 | "requires": { 651 | "p-defer": "^1.0.0" 652 | } 653 | }, 654 | "media-typer": { 655 | "version": "0.3.0", 656 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 657 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 658 | }, 659 | "mem": { 660 | "version": "4.3.0", 661 | "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", 662 | "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", 663 | "requires": { 664 | "map-age-cleaner": "^0.1.1", 665 | "mimic-fn": "^2.0.0", 666 | "p-is-promise": "^2.0.0" 667 | } 668 | }, 669 | "merge-descriptors": { 670 | "version": "1.0.1", 671 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 672 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 673 | }, 674 | "methods": { 675 | "version": "1.1.2", 676 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 677 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 678 | }, 679 | "mime": { 680 | "version": "1.4.1", 681 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 682 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 683 | }, 684 | "mime-db": { 685 | "version": "1.38.0", 686 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", 687 | "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" 688 | }, 689 | "mime-types": { 690 | "version": "2.1.22", 691 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", 692 | "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", 693 | "requires": { 694 | "mime-db": "~1.38.0" 695 | } 696 | }, 697 | "mimic-fn": { 698 | "version": "2.1.0", 699 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", 700 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" 701 | }, 702 | "minimatch": { 703 | "version": "3.0.4", 704 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 705 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 706 | "requires": { 707 | "brace-expansion": "^1.1.7" 708 | } 709 | }, 710 | "minimist": { 711 | "version": "0.0.8", 712 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 713 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 714 | }, 715 | "mkdirp": { 716 | "version": "0.5.1", 717 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 718 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 719 | "requires": { 720 | "minimist": "0.0.8" 721 | } 722 | }, 723 | "mocha": { 724 | "version": "6.1.4", 725 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.1.4.tgz", 726 | "integrity": "sha512-PN8CIy4RXsIoxoFJzS4QNnCH4psUCPWc4/rPrst/ecSJJbLBkubMiyGCP2Kj/9YnWbotFqAoeXyXMucj7gwCFg==", 727 | "requires": { 728 | "ansi-colors": "3.2.3", 729 | "browser-stdout": "1.3.1", 730 | "debug": "3.2.6", 731 | "diff": "3.5.0", 732 | "escape-string-regexp": "1.0.5", 733 | "find-up": "3.0.0", 734 | "glob": "7.1.3", 735 | "growl": "1.10.5", 736 | "he": "1.2.0", 737 | "js-yaml": "3.13.1", 738 | "log-symbols": "2.2.0", 739 | "minimatch": "3.0.4", 740 | "mkdirp": "0.5.1", 741 | "ms": "2.1.1", 742 | "node-environment-flags": "1.0.5", 743 | "object.assign": "4.1.0", 744 | "strip-json-comments": "2.0.1", 745 | "supports-color": "6.0.0", 746 | "which": "1.3.1", 747 | "wide-align": "1.1.3", 748 | "yargs": "13.2.2", 749 | "yargs-parser": "13.0.0", 750 | "yargs-unparser": "1.5.0" 751 | }, 752 | "dependencies": { 753 | "debug": { 754 | "version": "3.2.6", 755 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 756 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 757 | "requires": { 758 | "ms": "^2.1.1" 759 | } 760 | }, 761 | "ms": { 762 | "version": "2.1.1", 763 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 764 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 765 | } 766 | } 767 | }, 768 | "ms": { 769 | "version": "2.0.0", 770 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 771 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 772 | }, 773 | "multer": { 774 | "version": "1.4.1", 775 | "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.1.tgz", 776 | "integrity": "sha512-zzOLNRxzszwd+61JFuAo0fxdQfvku12aNJgnla0AQ+hHxFmfc/B7jBVuPr5Rmvu46Jze/iJrFpSOsD7afO8SDw==", 777 | "requires": { 778 | "append-field": "^1.0.0", 779 | "busboy": "^0.2.11", 780 | "concat-stream": "^1.5.2", 781 | "mkdirp": "^0.5.1", 782 | "object-assign": "^4.1.1", 783 | "on-finished": "^2.3.0", 784 | "type-is": "^1.6.4", 785 | "xtend": "^4.0.0" 786 | } 787 | }, 788 | "negotiator": { 789 | "version": "0.6.1", 790 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 791 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 792 | }, 793 | "nice-try": { 794 | "version": "1.0.5", 795 | "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 796 | "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" 797 | }, 798 | "node-environment-flags": { 799 | "version": "1.0.5", 800 | "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", 801 | "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", 802 | "requires": { 803 | "object.getownpropertydescriptors": "^2.0.3", 804 | "semver": "^5.7.0" 805 | } 806 | }, 807 | "node-fetch": { 808 | "version": "2.3.0", 809 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz", 810 | "integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==" 811 | }, 812 | "npm-run-path": { 813 | "version": "2.0.2", 814 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 815 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 816 | "requires": { 817 | "path-key": "^2.0.0" 818 | } 819 | }, 820 | "number-is-nan": { 821 | "version": "1.0.1", 822 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 823 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 824 | }, 825 | "object-assign": { 826 | "version": "4.1.1", 827 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 828 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 829 | }, 830 | "object-keys": { 831 | "version": "1.1.1", 832 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 833 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" 834 | }, 835 | "object.assign": { 836 | "version": "4.1.0", 837 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", 838 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", 839 | "requires": { 840 | "define-properties": "^1.1.2", 841 | "function-bind": "^1.1.1", 842 | "has-symbols": "^1.0.0", 843 | "object-keys": "^1.0.11" 844 | } 845 | }, 846 | "object.getownpropertydescriptors": { 847 | "version": "2.0.3", 848 | "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", 849 | "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", 850 | "requires": { 851 | "define-properties": "^1.1.2", 852 | "es-abstract": "^1.5.1" 853 | } 854 | }, 855 | "on-finished": { 856 | "version": "2.3.0", 857 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 858 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 859 | "requires": { 860 | "ee-first": "1.1.1" 861 | } 862 | }, 863 | "once": { 864 | "version": "1.4.0", 865 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 866 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 867 | "requires": { 868 | "wrappy": "1" 869 | } 870 | }, 871 | "os-locale": { 872 | "version": "3.1.0", 873 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", 874 | "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", 875 | "requires": { 876 | "execa": "^1.0.0", 877 | "lcid": "^2.0.0", 878 | "mem": "^4.0.0" 879 | } 880 | }, 881 | "p-defer": { 882 | "version": "1.0.0", 883 | "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", 884 | "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" 885 | }, 886 | "p-finally": { 887 | "version": "1.0.0", 888 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 889 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" 890 | }, 891 | "p-is-promise": { 892 | "version": "2.1.0", 893 | "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", 894 | "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" 895 | }, 896 | "p-limit": { 897 | "version": "2.2.0", 898 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", 899 | "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", 900 | "requires": { 901 | "p-try": "^2.0.0" 902 | } 903 | }, 904 | "p-locate": { 905 | "version": "3.0.0", 906 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 907 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 908 | "requires": { 909 | "p-limit": "^2.0.0" 910 | } 911 | }, 912 | "p-try": { 913 | "version": "2.2.0", 914 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 915 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" 916 | }, 917 | "parseurl": { 918 | "version": "1.3.2", 919 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 920 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 921 | }, 922 | "path-exists": { 923 | "version": "3.0.0", 924 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 925 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" 926 | }, 927 | "path-is-absolute": { 928 | "version": "1.0.1", 929 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 930 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 931 | }, 932 | "path-key": { 933 | "version": "2.0.1", 934 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 935 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" 936 | }, 937 | "path-to-regexp": { 938 | "version": "0.1.7", 939 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 940 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 941 | }, 942 | "process-nextick-args": { 943 | "version": "2.0.0", 944 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 945 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" 946 | }, 947 | "proxy-addr": { 948 | "version": "2.0.4", 949 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", 950 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", 951 | "requires": { 952 | "forwarded": "~0.1.2", 953 | "ipaddr.js": "1.8.0" 954 | } 955 | }, 956 | "pump": { 957 | "version": "3.0.0", 958 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 959 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 960 | "requires": { 961 | "end-of-stream": "^1.1.0", 962 | "once": "^1.3.1" 963 | } 964 | }, 965 | "qs": { 966 | "version": "6.5.2", 967 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 968 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 969 | }, 970 | "querystringify": { 971 | "version": "2.1.1", 972 | "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", 973 | "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" 974 | }, 975 | "range-parser": { 976 | "version": "1.2.0", 977 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 978 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 979 | }, 980 | "raw-body": { 981 | "version": "2.3.3", 982 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 983 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 984 | "requires": { 985 | "bytes": "3.0.0", 986 | "http-errors": "1.6.3", 987 | "iconv-lite": "0.4.23", 988 | "unpipe": "1.0.0" 989 | } 990 | }, 991 | "readable-stream": { 992 | "version": "1.1.14", 993 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 994 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 995 | "requires": { 996 | "core-util-is": "~1.0.0", 997 | "inherits": "~2.0.1", 998 | "isarray": "0.0.1", 999 | "string_decoder": "~0.10.x" 1000 | } 1001 | }, 1002 | "require-directory": { 1003 | "version": "2.1.1", 1004 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1005 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" 1006 | }, 1007 | "require-main-filename": { 1008 | "version": "2.0.0", 1009 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 1010 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" 1011 | }, 1012 | "requires-port": { 1013 | "version": "1.0.0", 1014 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 1015 | "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" 1016 | }, 1017 | "safe-buffer": { 1018 | "version": "5.1.2", 1019 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1020 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1021 | }, 1022 | "safer-buffer": { 1023 | "version": "2.1.2", 1024 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1025 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1026 | }, 1027 | "semver": { 1028 | "version": "5.7.0", 1029 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", 1030 | "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" 1031 | }, 1032 | "send": { 1033 | "version": "0.16.2", 1034 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 1035 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 1036 | "requires": { 1037 | "debug": "2.6.9", 1038 | "depd": "~1.1.2", 1039 | "destroy": "~1.0.4", 1040 | "encodeurl": "~1.0.2", 1041 | "escape-html": "~1.0.3", 1042 | "etag": "~1.8.1", 1043 | "fresh": "0.5.2", 1044 | "http-errors": "~1.6.2", 1045 | "mime": "1.4.1", 1046 | "ms": "2.0.0", 1047 | "on-finished": "~2.3.0", 1048 | "range-parser": "~1.2.0", 1049 | "statuses": "~1.4.0" 1050 | } 1051 | }, 1052 | "serve-static": { 1053 | "version": "1.13.2", 1054 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 1055 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 1056 | "requires": { 1057 | "encodeurl": "~1.0.2", 1058 | "escape-html": "~1.0.3", 1059 | "parseurl": "~1.3.2", 1060 | "send": "0.16.2" 1061 | } 1062 | }, 1063 | "set-blocking": { 1064 | "version": "2.0.0", 1065 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1066 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 1067 | }, 1068 | "setprototypeof": { 1069 | "version": "1.1.0", 1070 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 1071 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 1072 | }, 1073 | "shebang-command": { 1074 | "version": "1.2.0", 1075 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 1076 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 1077 | "requires": { 1078 | "shebang-regex": "^1.0.0" 1079 | } 1080 | }, 1081 | "shebang-regex": { 1082 | "version": "1.0.0", 1083 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 1084 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" 1085 | }, 1086 | "signal-exit": { 1087 | "version": "3.0.2", 1088 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1089 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 1090 | }, 1091 | "sprintf-js": { 1092 | "version": "1.0.3", 1093 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1094 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 1095 | }, 1096 | "statuses": { 1097 | "version": "1.4.0", 1098 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 1099 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 1100 | }, 1101 | "streamsearch": { 1102 | "version": "0.1.2", 1103 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", 1104 | "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" 1105 | }, 1106 | "string-width": { 1107 | "version": "2.1.1", 1108 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1109 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1110 | "requires": { 1111 | "is-fullwidth-code-point": "^2.0.0", 1112 | "strip-ansi": "^4.0.0" 1113 | } 1114 | }, 1115 | "string_decoder": { 1116 | "version": "0.10.31", 1117 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1118 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 1119 | }, 1120 | "strip-ansi": { 1121 | "version": "4.0.0", 1122 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1123 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1124 | "requires": { 1125 | "ansi-regex": "^3.0.0" 1126 | } 1127 | }, 1128 | "strip-eof": { 1129 | "version": "1.0.0", 1130 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 1131 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" 1132 | }, 1133 | "strip-json-comments": { 1134 | "version": "2.0.1", 1135 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1136 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 1137 | }, 1138 | "supports-color": { 1139 | "version": "6.0.0", 1140 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", 1141 | "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", 1142 | "requires": { 1143 | "has-flag": "^3.0.0" 1144 | } 1145 | }, 1146 | "type-is": { 1147 | "version": "1.6.16", 1148 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 1149 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 1150 | "requires": { 1151 | "media-typer": "0.3.0", 1152 | "mime-types": "~2.1.18" 1153 | } 1154 | }, 1155 | "typedarray": { 1156 | "version": "0.0.6", 1157 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1158 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" 1159 | }, 1160 | "unpipe": { 1161 | "version": "1.0.0", 1162 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1163 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 1164 | }, 1165 | "url-parse": { 1166 | "version": "1.4.4", 1167 | "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", 1168 | "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", 1169 | "requires": { 1170 | "querystringify": "^2.0.0", 1171 | "requires-port": "^1.0.0" 1172 | } 1173 | }, 1174 | "util-deprecate": { 1175 | "version": "1.0.2", 1176 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1177 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1178 | }, 1179 | "utils-merge": { 1180 | "version": "1.0.1", 1181 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1182 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 1183 | }, 1184 | "uuid": { 1185 | "version": "3.3.2", 1186 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 1187 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 1188 | }, 1189 | "vary": { 1190 | "version": "1.1.2", 1191 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1192 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 1193 | }, 1194 | "which": { 1195 | "version": "1.3.1", 1196 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1197 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1198 | "requires": { 1199 | "isexe": "^2.0.0" 1200 | } 1201 | }, 1202 | "which-module": { 1203 | "version": "2.0.0", 1204 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 1205 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" 1206 | }, 1207 | "wide-align": { 1208 | "version": "1.1.3", 1209 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 1210 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 1211 | "requires": { 1212 | "string-width": "^1.0.2 || 2" 1213 | } 1214 | }, 1215 | "wrap-ansi": { 1216 | "version": "2.1.0", 1217 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", 1218 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", 1219 | "requires": { 1220 | "string-width": "^1.0.1", 1221 | "strip-ansi": "^3.0.1" 1222 | }, 1223 | "dependencies": { 1224 | "ansi-regex": { 1225 | "version": "2.1.1", 1226 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 1227 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 1228 | }, 1229 | "is-fullwidth-code-point": { 1230 | "version": "1.0.0", 1231 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 1232 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 1233 | "requires": { 1234 | "number-is-nan": "^1.0.0" 1235 | } 1236 | }, 1237 | "string-width": { 1238 | "version": "1.0.2", 1239 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1240 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1241 | "requires": { 1242 | "code-point-at": "^1.0.0", 1243 | "is-fullwidth-code-point": "^1.0.0", 1244 | "strip-ansi": "^3.0.0" 1245 | } 1246 | }, 1247 | "strip-ansi": { 1248 | "version": "3.0.1", 1249 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1250 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1251 | "requires": { 1252 | "ansi-regex": "^2.0.0" 1253 | } 1254 | } 1255 | } 1256 | }, 1257 | "wrappy": { 1258 | "version": "1.0.2", 1259 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1260 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1261 | }, 1262 | "xtend": { 1263 | "version": "4.0.1", 1264 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 1265 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 1266 | }, 1267 | "y18n": { 1268 | "version": "4.0.0", 1269 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 1270 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" 1271 | }, 1272 | "yargs": { 1273 | "version": "13.2.2", 1274 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.2.tgz", 1275 | "integrity": "sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==", 1276 | "requires": { 1277 | "cliui": "^4.0.0", 1278 | "find-up": "^3.0.0", 1279 | "get-caller-file": "^2.0.1", 1280 | "os-locale": "^3.1.0", 1281 | "require-directory": "^2.1.1", 1282 | "require-main-filename": "^2.0.0", 1283 | "set-blocking": "^2.0.0", 1284 | "string-width": "^3.0.0", 1285 | "which-module": "^2.0.0", 1286 | "y18n": "^4.0.0", 1287 | "yargs-parser": "^13.0.0" 1288 | }, 1289 | "dependencies": { 1290 | "ansi-regex": { 1291 | "version": "4.1.0", 1292 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 1293 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" 1294 | }, 1295 | "string-width": { 1296 | "version": "3.1.0", 1297 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1298 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1299 | "requires": { 1300 | "emoji-regex": "^7.0.1", 1301 | "is-fullwidth-code-point": "^2.0.0", 1302 | "strip-ansi": "^5.1.0" 1303 | } 1304 | }, 1305 | "strip-ansi": { 1306 | "version": "5.2.0", 1307 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1308 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1309 | "requires": { 1310 | "ansi-regex": "^4.1.0" 1311 | } 1312 | } 1313 | } 1314 | }, 1315 | "yargs-parser": { 1316 | "version": "13.0.0", 1317 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.0.0.tgz", 1318 | "integrity": "sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw==", 1319 | "requires": { 1320 | "camelcase": "^5.0.0", 1321 | "decamelize": "^1.2.0" 1322 | } 1323 | }, 1324 | "yargs-unparser": { 1325 | "version": "1.5.0", 1326 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", 1327 | "integrity": "sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw==", 1328 | "requires": { 1329 | "flat": "^4.1.0", 1330 | "lodash": "^4.17.11", 1331 | "yargs": "^12.0.5" 1332 | }, 1333 | "dependencies": { 1334 | "get-caller-file": { 1335 | "version": "1.0.3", 1336 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", 1337 | "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" 1338 | }, 1339 | "require-main-filename": { 1340 | "version": "1.0.1", 1341 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", 1342 | "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" 1343 | }, 1344 | "yargs": { 1345 | "version": "12.0.5", 1346 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", 1347 | "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", 1348 | "requires": { 1349 | "cliui": "^4.0.0", 1350 | "decamelize": "^1.2.0", 1351 | "find-up": "^3.0.0", 1352 | "get-caller-file": "^1.0.1", 1353 | "os-locale": "^3.0.0", 1354 | "require-directory": "^2.1.1", 1355 | "require-main-filename": "^1.0.1", 1356 | "set-blocking": "^2.0.0", 1357 | "string-width": "^2.0.0", 1358 | "which-module": "^2.0.0", 1359 | "y18n": "^3.2.1 || ^4.0.0", 1360 | "yargs-parser": "^11.1.1" 1361 | } 1362 | }, 1363 | "yargs-parser": { 1364 | "version": "11.1.1", 1365 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", 1366 | "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", 1367 | "requires": { 1368 | "camelcase": "^5.0.0", 1369 | "decamelize": "^1.2.0" 1370 | } 1371 | } 1372 | } 1373 | } 1374 | } 1375 | } 1376 | -------------------------------------------------------------------------------- /nodejs/part1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blockchain-js", 3 | "version": "1.0.0", 4 | "description": "Learn to implement a blockchain in javascript ", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test" 8 | }, 9 | "keywords": [ 10 | "blockchain", 11 | "tutorial", 12 | "javascript" 13 | ], 14 | "author": "@joshorig", 15 | "license": "MIT", 16 | "dependencies": { 17 | "body-parser": "^1.18.3", 18 | "crypto-js": "^3.1.9-1", 19 | "express": "^4.16.4", 20 | "mocha": "^6.1.4", 21 | "multer": "^1.4.1", 22 | "node-fetch": "^2.3.0", 23 | "url-parse": "^1.4.4", 24 | "uuid": "^3.3.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /nodejs/part1/src/api.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | 4 | 5 | (async () => { 6 | 7 | const express = require('express'); 8 | const bodyParser = require('body-parser'); 9 | const multer = require('multer'); 10 | const Utils = require('./utils'); 11 | 12 | function getAPI(blockchain) { 13 | var app = express(); 14 | const requestParser = multer(); 15 | app.use(bodyParser.json()); 16 | //app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded 17 | 18 | app.post('/mine', requestParser.array(), async (req, res) => { 19 | const { data } = req.body || {}; 20 | 21 | if (!data) { 22 | res.status(400).send('Error: Must set data in request'); 23 | return; 24 | } 25 | 26 | await blockchain.checkLongestChain(); 27 | 28 | let block = blockchain.newBlock(data); 29 | 30 | const response = { 31 | message: 'Mined new block', 32 | ...block 33 | }; 34 | 35 | res.status(201).send(response); 36 | }); 37 | 38 | app.get('/blocks', (req, res) => { 39 | const response = { 40 | blocks: blockchain.blocks, 41 | count: blockchain.blocks.length 42 | }; 43 | 44 | res.send(response); 45 | }); 46 | 47 | app.get('/peers', (req, res) => { 48 | const response = { 49 | peers: blockchain.peers, 50 | count: blockchain.peers.length 51 | }; 52 | 53 | res.send(response); 54 | }); 55 | 56 | app.post('/peers/add', requestParser.array(), (req, res) => { 57 | const { peers } = req.body || []; 58 | 59 | if (!peers) { 60 | res.status(400).send('Error: Must supply list of peers in field peers'); 61 | return; 62 | } 63 | 64 | peers.forEach((peer) => { 65 | blockchain.registerPeer(peer); 66 | }); 67 | 68 | const response = { 69 | message: 'New peers have been added', 70 | peers: JSON.stringify([...blockchain.peers]), 71 | count: blockchain.peers.size 72 | }; 73 | 74 | res.status(201).send(response); 75 | }); 76 | 77 | app.get('/peers/check', async (req, res) => { 78 | let response; 79 | let result = await blockchain.checkLongestChain(); 80 | if(result) { 81 | response = { 82 | message: 'Chain is longest', 83 | newChain: blockchain.blocks 84 | }; 85 | } 86 | else { 87 | response = { 88 | message: 'Chain updated', 89 | newChain: blockchain.blocks 90 | }; 91 | } 92 | res.send(response); 93 | 94 | }); 95 | 96 | return app; 97 | } 98 | 99 | module.exports = { 100 | getAPI 101 | } 102 | })(); 103 | -------------------------------------------------------------------------------- /nodejs/part1/src/block.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (async () => { 4 | 5 | const Utils = require("./utils"); 6 | 7 | function Block(blockNumber,data,nonce,previousBlockHash){ 8 | this.blockNumber = blockNumber; 9 | this.data = data; 10 | this.nonce = nonce; 11 | this.previousBlockHash = previousBlockHash 12 | this.timestamp = Date.now() 13 | this.hash = ""; 14 | } 15 | 16 | Block.prototype.incrementNonce = function() { 17 | this.nonce++; 18 | this.hash = Utils.calculateHash(this); 19 | } 20 | 21 | Block.prototype.toString = function() { 22 | let blockDetails = { 23 | previousBlockHash: this.previousBlockHash, 24 | data: this.data, 25 | blockNumber: this.blockNumber, 26 | timestamp: this.timestamp, 27 | nonce: this.nonce, 28 | blockHash: this.hash 29 | } 30 | return JSON.stringify(blockDetails, Object.keys(blockDetails).sort()); 31 | }; 32 | 33 | module.exports = Block; 34 | })(); 35 | -------------------------------------------------------------------------------- /nodejs/part1/src/blockchain.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (async () => { 4 | 5 | const Consensus = require('./consensus') 6 | const parse = require('url-parse'); 7 | const Utils = require("./utils"); 8 | 9 | 10 | function Blockchain(consensus,blocks){ 11 | this.blocks = [] //the chain of blocks! 12 | if(blocks) 13 | { 14 | this.blocks = blocks; 15 | } 16 | this.peers = new Set(); //list of unique peers in the network 17 | this.consensus = consensus; 18 | //Create the genesis block 19 | this.newBlock("I am genesis!") 20 | } 21 | 22 | Blockchain.prototype.newBlock = function(data) { 23 | let previousBlockHash = ""; 24 | let newBlockNumber = 0 25 | if(this.blocks.length>0) { 26 | previousBlockHash = this.blocks[this.blocks.length-1].hash; 27 | newBlockNumber = this.blocks.length; 28 | } 29 | let block = this.consensus.mineBlock(newBlockNumber,data,previousBlockHash); 30 | this.blocks.push(block); 31 | return block; 32 | } 33 | 34 | Blockchain.prototype.isValid = function() { 35 | let currentblockNumber = 1; //start after the genesis block (blockNumber=0) 36 | while(currentblockNumber < this.blocks.length) { 37 | const currentBlock = this.blocks[currentblockNumber]; 38 | const previousBlock = this.blocks[currentblockNumber - 1]; 39 | 40 | // Check that previousBlockHash is correct 41 | if (currentBlock.previousBlockHash !== previousBlock.hash) { 42 | return false; 43 | } 44 | 45 | // check that the current blockHash is correct 46 | if(currentBlock.hash !== Utils.calculateHash(currentBlock)) { 47 | return false; 48 | } 49 | 50 | // Check that the nonce (proof of work result) is correct 51 | if (!this.consensus.validHash(currentBlock.hash)) { 52 | return false; 53 | } 54 | currentblockNumber++; 55 | } 56 | 57 | return true; 58 | } 59 | 60 | Blockchain.prototype.registerPeer = function(address) { 61 | const host = parse(address).host; 62 | this.peers.add(host); 63 | console.log("Registered peer: "+host) 64 | } 65 | 66 | Blockchain.prototype.checkLongestChain = async function() { 67 | let result = await this.consensus.checkLongestChain(this.peers,this.blocks.length); 68 | if(result.newBlocks) { 69 | this.blocks = result.newBlocks; 70 | console.log("Chain replaced: "+this.blocks) 71 | } 72 | return result.isLongestChain; 73 | } 74 | 75 | module.exports = Blockchain; 76 | })(); 77 | -------------------------------------------------------------------------------- /nodejs/part1/src/consensus.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (async () => { 4 | 5 | const Block = require('./block'); 6 | const Utils = require('./utils'); 7 | const fetch = require('node-fetch'); 8 | 9 | function Consensus(){ 10 | this.difficulty = 5; 11 | this.difficultyRegex = new RegExp('^0{'+this.difficulty+'}') 12 | } 13 | 14 | Consensus.prototype.mineBlock = function(blockNumber,data,previousBlockHash) { 15 | let block = new Block(blockNumber,data,0,previousBlockHash); //start the nonce at 0 16 | //while we have not got the correct number of leadings 0's (difficulty * 0) in our blockHash, keep incrimenting the blocks nonce 17 | while(!this.validHash(block.hash)) 18 | { 19 | block.incrementNonce(); 20 | } 21 | console.log("Mined new block: "+block.toString()); 22 | return block; 23 | } 24 | 25 | Consensus.prototype.validHash = function(hash) { 26 | return this.difficultyRegex.test(hash); 27 | } 28 | 29 | Consensus.prototype.checkLongestChain = function(peers,length) 30 | { 31 | let promises = []; 32 | 33 | peers.forEach((host) => { 34 | promises.push( 35 | fetch('http://'+host+'/blocks') 36 | .then(res => { 37 | if (res.ok) { 38 | return res.json(); 39 | } 40 | }) 41 | .then(json => json) 42 | ); 43 | }); 44 | 45 | return Promise.all(promises).then((chains) => { 46 | let newBlocks = null; 47 | let longestLength = length; 48 | 49 | chains.forEach(({ blocks }) => { 50 | // Check if the length is longer and the chain is valid 51 | if (blocks.length > longestLength && this.isChainValid(blocks)) { 52 | longestLength = blocks.length; 53 | newBlocks = blocks; 54 | } 55 | }); 56 | 57 | return { isLongestChain: !newBlocks, newBlocks: newBlocks }; 58 | }); 59 | } 60 | 61 | Consensus.prototype.isChainValid = function(blocks) { 62 | let currentblockNumber = 1; //start after the genesis block (blockNumber=0) 63 | while(currentblockNumber < blocks.length) { 64 | const currentBlock = blocks[currentblockNumber]; 65 | const previousBlock = blocks[currentblockNumber - 1]; 66 | 67 | // Check that previousBlockHash is correct 68 | if (currentBlock.previousBlockHash !== previousBlock.hash) { 69 | return false; 70 | } 71 | // check that the current blockHash is correct 72 | if(currentBlock.hash !== Utils.calculateHash(currentBlock)) { 73 | return false; 74 | } 75 | // Check that the nonce (proof of work result) is correct 76 | if (!this.validHash(currentBlock.hash)) { 77 | return false; 78 | } 79 | currentblockNumber++; 80 | } 81 | return true; 82 | } 83 | 84 | module.exports = Consensus; 85 | })(); 86 | -------------------------------------------------------------------------------- /nodejs/part1/src/server.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (async () => { 4 | 5 | const Utils = require("./utils"); 6 | const Api = require("./api"); 7 | const Blockchain = require('../src/blockchain'); 8 | const Consensus = require('../src/consensus'); 9 | 10 | const DEFAULT_PORT = 5000; 11 | const args = Utils.parseArgs(); 12 | 13 | const port = args.port || DEFAULT_PORT; 14 | 15 | let app = Api.getAPI(new Blockchain(new Consensus())); 16 | app.listen(port) 17 | console.log("Blockchain server listening on port: "+port) 18 | 19 | 20 | })(); 21 | -------------------------------------------------------------------------------- /nodejs/part1/src/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (async () => { 4 | 5 | const SHA256 = require("crypto-js/sha256"); 6 | const UUIDV4 = require("uuid/v4"); 7 | 8 | function getSHA256HexString(input) { 9 | return SHA256(input).toString(); 10 | } 11 | 12 | function parseArgs() { 13 | return process.argv 14 | .slice(2) 15 | .map(arg => arg.split('=')) 16 | .reduce((args, [value, key]) => { 17 | args[value] = key; 18 | return args; 19 | }, {}); 20 | } 21 | 22 | function calculateHash(block) { 23 | let blockDetails = { 24 | previousBlockHash: block.previousBlockHash, 25 | data: block.data, 26 | blockNumber: block.blockNumber, 27 | timestamp: block.timestamp, 28 | nonce: block.nonce 29 | } 30 | return getSHA256HexString(JSON.stringify(blockDetails, Object.keys(blockDetails).sort())); 31 | } 32 | 33 | 34 | module.exports = { 35 | getSHA256HexString, 36 | parseArgs, 37 | calculateHash 38 | }; 39 | 40 | })(); 41 | -------------------------------------------------------------------------------- /nodejs/part1/test/block.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (async () => { 4 | // ######################################################################################################## 5 | // ######################################################################################################## 6 | // IMPORTS 7 | const assert = require('assert'); 8 | const Block = require('../src/block') 9 | 10 | // ######################################################################################################## 11 | // ######################################################################################################## 12 | // TESTS 13 | describe('Block object tests', function() { 14 | // it('Should calculate hash on creation', function() { 15 | // let block = new Block(0,"data",0,"string"); 16 | // assert.strictEqual(block.hash.length, 64); 17 | // }); 18 | 19 | }); 20 | })(); 21 | -------------------------------------------------------------------------------- /nodejs/part1/test/blockchain.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (async () => { 4 | // ######################################################################################################## 5 | // ######################################################################################################## 6 | // IMPORTS 7 | const assert = require('assert'); 8 | const Blockchain = require('../src/blockchain'); 9 | const Consensus = require('../src/consensus'); 10 | const Block = require('../src/block'); 11 | 12 | // ######################################################################################################## 13 | // ######################################################################################################## 14 | // 15 | var blockchain; 16 | var consensus; 17 | const BLOCK_TIMEOUT = 60000; 18 | 19 | // ######################################################################################################## 20 | // ######################################################################################################## 21 | // TESTS 22 | describe('Blockchain tests', function() { 23 | this.timeout(BLOCK_TIMEOUT*4); 24 | 25 | beforeEach(async function() { 26 | consensus = new Consensus(); 27 | blockchain = new Blockchain(consensus); 28 | }); 29 | 30 | it('Should create a genesis block when created', function() { 31 | 32 | assert.strictEqual(blockchain.blocks.length, 1); 33 | assert.strictEqual(blockchain.blocks[0].data, "I am genesis!"); 34 | assert.strictEqual(blockchain.blocks[0].blockNumber, 0); 35 | }); 36 | 37 | it('Should add new valid block', function() { 38 | blockchain.newBlock("some data"); 39 | assert.strictEqual(blockchain.blocks.length, 2); 40 | assert.strictEqual(blockchain.blocks[1].data, "some data"); 41 | assert.strictEqual(blockchain.blocks[1].blockNumber, 1); 42 | assert.strictEqual(blockchain.isValid(), true); 43 | blockchain.newBlock("some more data"); 44 | assert.strictEqual(blockchain.blocks.length, 3); 45 | assert.strictEqual(blockchain.blocks[2].data, "some more data"); 46 | assert.strictEqual(blockchain.blocks[2].blockNumber, 2); 47 | assert.strictEqual(blockchain.isValid(), true); 48 | }); 49 | 50 | it('Should fail to validate blockchain if new block addded with incorrect previous hash', function() { 51 | blockchain.newBlock("some data"); 52 | assert.strictEqual(blockchain.isValid(), true); 53 | let block = consensus.mineBlock(3,"some more data","INVALID_HASH"); 54 | blockchain.blocks.push(block); 55 | assert.strictEqual(blockchain.isValid(), false); 56 | }); 57 | 58 | it('Should fail to validate blockchain if data in a previous block is changed', function() { 59 | blockchain.newBlock("some data"); 60 | assert.strictEqual(blockchain.isValid(), true); 61 | blockchain.newBlock("some more data"); 62 | assert.strictEqual(blockchain.isValid(), true); 63 | blockchain.blocks[1].data = "invalid data"; 64 | assert.strictEqual(blockchain.isValid(), false); 65 | }); 66 | 67 | it('Should fail to validate blockchain if a previous block is swaped for another', function() { 68 | blockchain.newBlock("some data"); 69 | assert.strictEqual(blockchain.isValid(), true); 70 | blockchain.newBlock("some more data"); 71 | assert.strictEqual(blockchain.isValid(), true); 72 | let block = consensus.mineBlock(2,"some data",blockchain.blocks[0].hash); //regenerating the block should result in a different block hash 73 | blockchain.blocks[1] = block; 74 | assert.strictEqual(blockchain.isValid(), false); 75 | }); 76 | 77 | }); 78 | })(); 79 | -------------------------------------------------------------------------------- /nodejs/part1/test/consensus.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (async () => { 4 | // ######################################################################################################## 5 | // ######################################################################################################## 6 | // IMPORTS 7 | const assert = require('assert'); 8 | const Consensus = require('../src/consensus') 9 | 10 | // ######################################################################################################## 11 | // ######################################################################################################## 12 | // 13 | var consensus; 14 | const BLOCK_TIMEOUT = 60000; 15 | 16 | // ######################################################################################################## 17 | // ######################################################################################################## 18 | // TESTS 19 | describe('Consensus tests', function() { 20 | 21 | beforeEach(async function() { 22 | consensus = new Consensus(); 23 | }); 24 | 25 | it('Should correctly valdidate valid hash', function() { 26 | let hashString = "000005622474hwbkvrwhj" 27 | assert.strictEqual(consensus.validHash(hashString), true); 28 | }); 29 | 30 | it('Should fail to valdidate invalid hash', function() { 31 | let hashString = "0005622474hwbkvrwhj" 32 | assert.strictEqual(consensus.validHash(hashString), false); 33 | }); 34 | 35 | it('Should create a block with a valid hash', function() { 36 | this.timeout(BLOCK_TIMEOUT); 37 | let block = consensus.mineBlock(0,"some data","PREVIOUS_HASH"); 38 | assert.strictEqual(consensus.validHash(block.hash), true); 39 | }); 40 | 41 | }); 42 | })(); 43 | --------------------------------------------------------------------------------