diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..505372b --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Blockchain implemented by JavaScript + +Inspired by [learn-blockchains-by-building-one](https://hackernoon.com/learn-blockchains-by-building-one-117428612f46) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..cff6ab6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,455 @@ +{ + "name": "blockchain-js", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/node": { + "version": "14.0.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.23.tgz", + "integrity": "sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "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==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "crypto-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz", + "integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "ts-node": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", + "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typescript": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz", + "integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.2.0.tgz", + "integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..00e8428 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "blockchain-ts", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "ts-node src/main.ts" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.19.0", + "crypto-js": "^4.0.0", + "express": "^4.17.1", + "uuid": "^8.2.0" + }, + "devDependencies": { + "@types/node": "^14.0.23", + "ts-node": "^8.10.2", + "typescript": "^3.9.6" + } +} diff --git a/src/Blockchain.ts b/src/Blockchain.ts new file mode 100644 index 0000000..289ca41 --- /dev/null +++ b/src/Blockchain.ts @@ -0,0 +1,127 @@ +/* +* @Author: Just be free +* @Date: 2020-07-16 14:06:38 +* @Last Modified by: Just be free +* @Last Modified time: 2020-07-17 18:58:16 +* @E-mail: justbefree@126.com +*/ +import { default as HttpAsync } from "./http"; +import { Transactions } from "../types/transactions"; +import { Block } from "../types/block"; +import { Node } from "../types/node"; +import { Callback } from "../types/callback"; +import sha256 from "crypto-js/sha256"; +const http = new HttpAsync(); +class Blockchain { + + private currentTransactions: Array; + private chain: Array; + private nodes: Array; + + constructor() { + this.chain = []; + this.currentTransactions = []; + this.nodes = []; + this.newBlock(0, "100"); + } + + public getChain(): Array { + return this.chain; + } + public getChainLength(): number { + return this.chain.length; + } + + public isNodeExists(node: Node): boolean { + return this.nodes.indexOf(node) > -1; + } + + public registerNodes(node: Node): void { + if (!this.isNodeExists(node)) { + this.nodes.push(node); + } + } + + public getNodes(): Array { + return this.nodes; + } + + public newBlock(proof: number, previousHash: string): Block { + const chainLength: number = this.chain.length; + const block: Block = { + index: chainLength + 1, + timestamp: Date.now(), + transactions: this.currentTransactions, + proof: proof, + previousHash: previousHash || Blockchain.hash(this.chain[chainLength - 1]) + }; + this.currentTransactions = []; + this.chain.push(block); + return block; + } + static hash(block: Block): string { + const blockString: string = JSON.stringify(block); + return sha256(blockString).toString(); + } + static validProof(lastProof: number, proof: number, lastHash: string): boolean { + const guess: string = `${lastProof}${proof}${lastHash}`; + const guessHash: string = sha256(guess).toString(); + // console.log("guessHash----------", guessHash); + return guessHash.substr(-4) === "0000"; + } + public proofOfWork(lastBlock): number { + const lastProof: number = lastBlock["proof"]; + const lastHash: string = Blockchain.hash(lastBlock); + let proof = 1; + while(!Blockchain.validProof(lastProof, proof, lastHash)) { + proof += 1; + } + return proof; + } + public newTransaction(transaction: Transactions): number { + this.currentTransactions.push(transaction); + const block: Block = this.lastBlock(); + return block["index"] + 1; + } + public lastBlock(): Block { + return this.chain[this.chain.length - 1]; + } + public validChain(chain: Array): boolean { + let lastBlock: Block = chain[0]; + let currentIndex: number = 1; + while (currentIndex < chain.length) { + let block: Block = chain[currentIndex]; + let lastBlockHash: string = Blockchain.hash(lastBlock); + if (lastBlockHash !== block["previousHash"] || !Blockchain.validProof(lastBlock["proof"], block["proof"], lastBlockHash)) { + return false; + } + lastBlock = block; + currentIndex += 1; + } + return true; + } + + private getLongestChain(callback: Callback | null): void { + const neighbours: Array = this.nodes; + let maxLength: number = this.chain.length; + let newChain: Array = this.chain; + let isNewChain: boolean = false; + Promise.all(neighbours.map(node => http.get(`${node}/chain`))).then(res => { + res.forEach(chainObjcet => { + if (chainObjcet.length > maxLength && this.validChain(chainObjcet.chain)) { + maxLength = chainObjcet.length; + newChain = chainObjcet.chain; + isNewChain = true; + } + }); + callback({ chain: newChain, isNewChain }); + }).catch(err => { + callback({ chain: newChain, isNewChain, exceptions: err }); + }); + } + + public resolveConflicts(callback: Callback | null): void { + this.getLongestChain(callback); + } +}; +export default Blockchain; diff --git a/src/http.ts b/src/http.ts new file mode 100644 index 0000000..5508293 --- /dev/null +++ b/src/http.ts @@ -0,0 +1,39 @@ +/* +* @Author: Just be free +* @Date: 2020-07-16 16:34:18 +* @Last Modified by: Just be free +* @Last Modified time: 2020-07-17 19:07:20 +* @E-mail: justbefree@126.com +*/ +import * as http from "http"; + +class HttpAsync { + constructor() {} + public async get(url: string): Promise { + const promise = new Promise((resolve, reject) => { + http.get(url, res => { + const { statusCode } = res; + if (statusCode !== 200) { + res.resume(); + reject("Request failed"); + } else { + res.setEncoding("utf8"); + let rowData: string = ""; + res.on("data", (chunk) => { rowData += chunk; }); + res.on("end", () => { + try { + const parsedData = JSON.parse(rowData); + resolve(parsedData); + } catch (e) { + console.error(e); + reject(e); + } + }); + } + }); + }); + return promise; + } +}; + +export default HttpAsync; diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..9b7983b --- /dev/null +++ b/src/main.ts @@ -0,0 +1,98 @@ +/* +* @Author: Just be free +* @Date: 2020-07-16 14:00:35 +* @Last Modified by: Just be free +* @Last Modified time: 2020-07-17 18:58:58 +* @E-mail: justbefree@126.com +*/ +const bodyParser = require("body-parser"); +import { Block } from "../types/block"; +import { Transactions } from "../types/transactions"; +import { Node } from "../types/node"; +import { default as Blockchain } from "./Blockchain"; +import { default as express } from "express"; +import { v4 as uuidv4 } from 'uuid'; +const app = express(); +const blockchain = new Blockchain(); +app.get("/mine", (req, res) => { + // 挖矿 + const lastBlock: Block = blockchain.lastBlock(); + const proof: number = blockchain.proofOfWork(lastBlock); + const transaction: Transactions = { amount: 5, recipient: uuidv4(), sender: "000" }; + blockchain.newTransaction(transaction); + const previousHash: string = Blockchain.hash(lastBlock); + const block: Block = blockchain.newBlock(proof, previousHash); + const response = { + message: "New Block Forged", + index: block["index"], + transactions: block["transactions"], + proof: block["proof"], + previousHash: block["previousHash"] + }; + res.send(response); +}); + +app.get("/chain", (req, res) => { + req.accepts('application/json'); + const response = { + chain: blockchain.getChain(), + length: blockchain.getChainLength() + }; + res.send(response); +}); + +app.post("/transactions/new", bodyParser.json(), (req, res) => { + const required = ["sender", "recipient", "amount"]; + const transaction: Transactions = req.body; + let pass: boolean = true; + let message: string; + required.forEach(param => { + if (!(param in transaction)) { + pass = false; + message = `${param} is required`; + } + }); + if (pass) { + const index: number = blockchain.newTransaction(transaction); + message = `Transaction will be added to Block ${index}`; + res.send({ message }); + } else { + res.send({ message }); + } +}); + +app.post("/nodes/register", bodyParser.json(), (req, res) => { + const nodes: Array = req.body.nodes; + let message: string; + if (!nodes) { + message = "nodes is required"; + } + if (!nodes.length) { + message = "nodes type must be an Array"; + } + + nodes.forEach(node => { + if (blockchain.isNodeExists(node)) { + message = `The Node ${node} is already exists`; + } else { + blockchain.registerNodes(node); + message = "New nodes have been added"; + } + }); + res.send({ message, totalNodes: blockchain.getNodes() }); +}); + + +app.get("/nodes/resolve", (req, res) => { + blockchain.resolveConflicts((chain) => { + if (chain.isNewChain) { + res.send({ message: "Our chain was replaced", chain }); + } else { + res.send({ message: "Our chain is authoritative", chain }); + } + }); +}); + +app.listen(8000, () => { + console.log("Server is running at 8000"); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b8274e4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": false, + "target": "es6", + "lib": ["es2015", "dom"], + "allowJs": true, + "resolveJsonModule": true, + "esModuleInterop": true, + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/types/block.ts b/types/block.ts new file mode 100644 index 0000000..47f82d2 --- /dev/null +++ b/types/block.ts @@ -0,0 +1,16 @@ +/* +* @Author: Just be free +* @Date: 2020-07-16 14:14:44 +* @Last Modified by: Just be free +* @Last Modified time: 2020-07-16 18:09:01 +* @E-mail: justbefree@126.com +*/ + +import { Transactions } from "./transactions"; +export interface Block { + index: number; + previousHash: string; + proof: number; + timestamp: number; + transactions: Array; +}; diff --git a/types/callback.ts b/types/callback.ts new file mode 100644 index 0000000..c0b8286 --- /dev/null +++ b/types/callback.ts @@ -0,0 +1,9 @@ +/* +* @Author: Just be free +* @Date: 2020-07-17 17:52:21 +* @Last Modified by: Just be free +* @Last Modified time: 2020-07-17 17:53:37 +* @E-mail: justbefree@126.com +*/ + +export type Callback = (args: any) => any; diff --git a/types/index.ts b/types/index.ts new file mode 100644 index 0000000..7256b06 --- /dev/null +++ b/types/index.ts @@ -0,0 +1,8 @@ +/* +* @Author: Just be free +* @Date: 2020-07-16 14:13:00 +* @Last Modified by: Just be free +* @Last Modified time: 2020-07-16 14:13:07 +* @E-mail: justbefree@126.com +*/ + diff --git a/types/node.ts b/types/node.ts new file mode 100644 index 0000000..513a321 --- /dev/null +++ b/types/node.ts @@ -0,0 +1,11 @@ +/* +* @Author: Just be free +* @Date: 2020-07-16 14:42:17 +* @Last Modified by: Just be free +* @Last Modified time: 2020-07-16 14:42:53 +* @E-mail: justbefree@126.com +*/ + +export interface Node { + [propName: string]: any; +}; \ No newline at end of file diff --git a/types/transactions.ts b/types/transactions.ts new file mode 100644 index 0000000..f459997 --- /dev/null +++ b/types/transactions.ts @@ -0,0 +1,13 @@ +/* +* @Author: Just be free +* @Date: 2020-07-16 14:10:33 +* @Last Modified by: Just be free +* @Last Modified time: 2020-07-16 15:16:05 +* @E-mail: justbefree@126.com +*/ + +export interface Transactions { + amount: number; + recipient: string; + sender: string; +};