From cd678370bd3f9af31ac51d7319bc4d4117efc1e9 Mon Sep 17 00:00:00 2001 From: axatbhardwaj Date: Fri, 16 Aug 2024 20:51:17 +0530 Subject: [PATCH 1/7] draft 1 --- .gitignore | 130 +++++++++++ centralServer.js | 197 ++++++++++++++++ fund-wallets.txt | 22 ++ minerServer.js | 258 +++++++++++++++++++++ package.json | 7 + pnpm-lock.yaml | 567 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1181 insertions(+) create mode 100644 .gitignore create mode 100644 centralServer.js create mode 100644 fund-wallets.txt create mode 100644 minerServer.js create mode 100644 package.json create mode 100644 pnpm-lock.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6bba59 --- /dev/null +++ b/.gitignore @@ -0,0 +1,130 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/centralServer.js b/centralServer.js new file mode 100644 index 0000000..3813e29 --- /dev/null +++ b/centralServer.js @@ -0,0 +1,197 @@ +const WebSocket = require('ws'); +const crypto = require('crypto'); +const fs = require('fs'); + +const wss = new WebSocket.Server({ port: 8080 }); + +let blockchain = []; // Store blockchain in-memory +let miners = []; +let difficulty = 4; // Initial difficulty: requires hash to start with '0000' +const targetBlockTime = 30000; // Target block time: 30 seconds +const adjustmentInterval = 5; // Adjust difficulty every 5 blocks +let balances = {}; // Ledger to track account balances +let miningInProgress = false; // Flag to prevent multiple blocks being mined in the same interval + +// Function to create key pairs and fund initial balances +async function initializeBlockchain() { + const keypairs = []; + const initialBalance = 10000000; + const wallets = []; + + for (let i = 0; i < 4; i++) { + const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { + modulusLength: 2048, + publicKeyEncoding: { type: 'spki', format: 'pem' }, + privateKeyEncoding: { type: 'pkcs8', format: 'pem' } + }); + + keypairs.push({ publicKey, privateKey }); + + // Assign the initial balance to the wallet + balances[publicKey] = initialBalance; + + wallets.push({ publicKey: publicKey, privateKey: privateKey, balance: initialBalance }); + } + console.log('Wallets:', wallets); + + + console.log('Balances:', balances); + console.log("starting the file save"); + + // Save the wallet information to a file + fs.writeFileSync('fund-wallets.txt', JSON.stringify(wallets, null, 2)); + + + const genesisBlock = { + index: 0, + previousHash: '0'.repeat(64), + timestamp: Date.now(), + transactions: [], + nonce: 0 + }; + + blockchain.push(genesisBlock); +} + + + +wss.on('connection', (ws) => { + miners.push(ws); + console.log('New miner connected'); + + //check if the blockchain is empty + if (blockchain.length === 0) { + //intilizing the blockchain + initializeBlockchain(); + console.log('Blockchain initialized'); + } + + ws.on('message', (message) => { + const data = JSON.parse(message); + + if (data.type === 'request_blockchain') { + ws.send(JSON.stringify({ type: 'blockchain', blockchain, difficulty, balances })); + } else if (data.type === 'block') { + if (!miningInProgress && validateAndAddBlock(data.block)) { + miningInProgress = true; + console.log('Block mined and accepted, stopping mining'); + + // Broadcast the new block to all miners + miners.forEach(miner => { + if (miner !== ws) { + miner.send(JSON.stringify({ type: 'block', block: data.block, difficulty, balances })); + } + }); + + // Notify miners to stop mining + miners.forEach(miner => { + miner.send(JSON.stringify({ type: 'block_finalized', blockIndex: data.block.index })); + }); + + // Adjust difficulty if necessary + if (blockchain.length % adjustmentInterval === 0) { + adjustDifficulty(); + } + } else { + console.log('Block rejected due to mining already in progress or validation failure'); + } + } else if (data.type === 'transaction') { + // Broadcast the transaction to all miners + miners.forEach(miner => { + if (miner !== ws) { + miner.send(JSON.stringify({ type: 'transaction', transaction: data.transaction })); + } + }); + console.log('Transaction broadcasted to all miners'); + } + }); + + ws.on('close', () => { + miners = miners.filter(miner => miner !== ws); + console.log('Miner disconnected'); + }); +}); + +// Function to validate and add a block to the blockchain +function validateAndAddBlock(newBlock) { + const lastBlock = blockchain[blockchain.length - 1]; + + if (lastBlock && lastBlock.hash === newBlock.previousHash && validateBlock(newBlock)) { + blockchain.push(newBlock); + updateBalances(newBlock.transactions); + return true; + } else if (!lastBlock && newBlock.index === 0) { + // Genesis block: the first block in the chain + blockchain.push(newBlock); + updateBalances(newBlock.transactions); + return true; + } + return false; +} + +// Function to validate a block based on current difficulty +function validateBlock(block) { + const hash = crypto.createHash('sha256') + .update(block.previousHash + JSON.stringify(block.transactions) + block.nonce) + .digest('hex'); + return hash.substring(0, difficulty) === '0'.repeat(difficulty); +} + +// Function to adjust the difficulty +function adjustDifficulty() { + const lastBlock = blockchain[blockchain.length - 1]; + const blocksToConsider = blockchain.slice(-adjustmentInterval); + + // Calculate the average block time for the last 'adjustmentInterval' blocks + const totalBlockTime = blocksToConsider.reduce((total, block, index) => { + if (index > 0) { + return total + (block.timestamp - blocksToConsider[index - 1].timestamp); + } + return total; + }, 0); + + const averageBlockTime = totalBlockTime / (blocksToConsider.length - 1); + + // Adjust difficulty based on the average block time + if (averageBlockTime < targetBlockTime) { + difficulty++; + console.log('Difficulty increased to:', difficulty); + } else if (averageBlockTime > targetBlockTime && difficulty > 1) { + difficulty--; + console.log('Difficulty decreased to:', difficulty); + } +} + +// Function to update balances based on the transactions in a new block +function updateBalances(transactions) { + transactions.forEach(tx => { + const { sender, recipient, amount } = tx; + + // Deduct the amount from the sender's balance + if (balances[sender] !== undefined) { + balances[sender] -= amount; + } else { + balances[sender] = -amount; // Allow negative balance (overdraft) for simplicity + } + + // Add the amount to the recipient's balance + if (balances[recipient] !== undefined) { + balances[recipient] += amount; + } else { + balances[recipient] = amount; + } + }); + + console.log('Updated balances:', balances); +} + +// Start mining process at regular intervals +setInterval(() => { + miningInProgress = false; + console.log('New mining round started'); + miners.forEach(miner => { + miner.send(JSON.stringify({ type: 'start_mining', difficulty })); + }); +}, targetBlockTime); + +console.log('Central server running on ws://localhost:8080'); diff --git a/fund-wallets.txt b/fund-wallets.txt new file mode 100644 index 0000000..f90ce51 --- /dev/null +++ b/fund-wallets.txt @@ -0,0 +1,22 @@ +[ + { + "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4gHlkdQXC7ufn2sfhasP\ns53C7ThcTy2HWp2v65Erjd6+1AEkjOCew10w0hqx5cmjqJ9Mc3PFtxlh+YMTpRf1\nboB/TI4Jm/vyD7aYMJpAJdqI+Rj3Lu8lIIsR7UMZ7s6hsd5xLzvAddb3xnqIza6J\nXGQjdrVWCrpvh07X90TMOU+TZ6hFAmcjCao1sMEaf5jWkY3DQODDeOP2fTR90q57\n5Mrxd8MCdqtbVti1QtEy1eEIq9dKzlv9EySu0M49r4dg3sD841SCRzkCmcfRId4r\nPgZ58fJ8rrgreuiXUIdgIFGYMEwgoR8ervWnYGTRc2tjB7YnITtl5AQsWS8Gheeq\nuwIDAQAB\n-----END PUBLIC KEY-----\n", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDiAeWR1BcLu5+f\nax+Fqw+zncLtOFxPLYdana/rkSuN3r7UASSM4J7DXTDSGrHlyaOon0xzc8W3GWH5\ngxOlF/VugH9Mjgmb+/IPtpgwmkAl2oj5GPcu7yUgixHtQxnuzqGx3nEvO8B11vfG\neojNrolcZCN2tVYKum+HTtf3RMw5T5NnqEUCZyMJqjWwwRp/mNaRjcNA4MN44/Z9\nNH3SrnvkyvF3wwJ2q1tW2LVC0TLV4Qir10rOW/0TJK7Qzj2vh2DewPzjVIJHOQKZ\nx9Eh3is+Bnnx8nyuuCt66JdQh2AgUZgwTCChHx6u9adgZNFza2MHtichO2XkBCxZ\nLwaF56q7AgMBAAECggEADutCzRcRNGLncmhptXL+1NkFApAgtntSFDRqO08jGbjf\n+PJgXXUAa0i4uHiEUMpi5XZ/30kqU+AMPHfKHAZSWxUxZ2DNl+s65eL5oI+2If7k\njCj5PDMTd4FRUXZcp7Z31wbQ4Dt+BL6y2Xps6G2X6gOUPGRTsIlGY8oGbe9ahIKG\nmxxlTHd8wEZrMqHoAyTBGLZJxOmNVsrOA7C34b6t5916PEWVdgirMcMneIQadW+M\nCiUl7lr5kMvazJ7T3AmUBQeO0dYTrkiM1udD2Ewp4XsyIJWZafuJMed4BT/KOa+Z\nankS5NUHzj2qfYMmEHu/9LtDIK+ac17D1RIXtudr8QKBgQD+zlE+SM/1frCI3yeI\nrFFL0OyT/0aqs3eXsT3/GR9WRjg4DGQWhLMpKSj4MSfMnL/g66ywLTwnnm5kSZ6w\nCwMRiR+0NlRbt9NCpbrzij6+FJmxj79LjZdTzV+Wo63jhPAa+HGVQl/xKWYehaXZ\nYU89WxxEpu7wZ/WhFH1l8KjfSwKBgQDjEQfdmMVj8EXBd7yfQ9BrNtuZ4Jr1q4ad\nq5SbFORPbwNtNQ8cVVYGG3Ck+YspmKWrNz6cRrX5i90u2v/Ue5xrlYY67wu34cjh\nSow5gbpC8SAdigz3EHGnprXkI9n2LthL1SGK1U7WEKcXmmUglnmoItHGE6+PCjEX\nEZI/bZSMUQKBgCEnGP0AnkYz0KZOWqd2vDEgItfyu/AT37iV8BoT27bu9fuc6Rzg\n8Sqm11TLmIEP2AtcvqBBknUZMQXNal8/X4vVipF5DCrO1Sbzx0pOr3Et3v1yVZJv\n/sOBkjFYZJSYxGDN+IFSHIeVOls4SmbpOVUjWJ86FRakgBoQP2yXDnHXAoGAC0jY\nZDnobWwOe2vw8tVS8VEbns0bxmPM398cFoz2P8BbojzF0HmpOcp5UamZMesFKLop\nIWkeOBWsW1zE5BF0l6ApuJoSsgPFkPS8fUsmL5d+KhmnRUwK6fMODRV/Co4Pb81a\nymSJc3gGZdTle7Yeu61nUXm++tPd1rCn3QEcJdECgYBgjGwwk7gj631erQlSQGFx\n2L9BetUkaqp+/wVau0mu/rHtHxqwT3r6jMWW497gh25iz/ZSXXlBneqvheECXBQ8\nzCeeQ+B1vPSYpPRGSeaw0rv8Qja+p8UYC14nwW26YpWRlNyLAhfUWDF8hzcybC5J\nZLcKrRIpHCwv1h3YMb7n1A==\n-----END PRIVATE KEY-----\n", + "balance": 10000000 + }, + { + "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApS+ZimJ5CthLCmYHr86F\nVwJZG5vmHNCrYXU77A8ASxR1y+ieVfXmaMlI7rltApB/rN047hNwu/CdVbJFEwcA\n1tZzXJEmlsgn6iuoyMaRHhH1zo+n7RRwCs+j8o6nJdCBRmWT2kojx13BO3uNzbE5\nJPsIN8+KTwX5MQR8gjdUTmbSl0mHAb6YQpCIG7df0E5nJ87w6+ItPh+XwBPxQulC\neDjGT+9b/+elv3Xc/yd574J0PvvE0jBZhQLGLZn1StK2bNPyXcmTLB1RDayLsjRJ\nhZx7zGenbKMwWc5rovwE+FNBUlgl7d/Jz1X5uLUBEsivQlOIrMVE9FosJcFpiEVR\nYwIDAQAB\n-----END PUBLIC KEY-----\n", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQClL5mKYnkK2EsK\nZgevzoVXAlkbm+Yc0KthdTvsDwBLFHXL6J5V9eZoyUjuuW0CkH+s3TjuE3C78J1V\nskUTBwDW1nNckSaWyCfqK6jIxpEeEfXOj6ftFHAKz6Pyjqcl0IFGZZPaSiPHXcE7\ne43NsTkk+wg3z4pPBfkxBHyCN1ROZtKXSYcBvphCkIgbt1/QTmcnzvDr4i0+H5fA\nE/FC6UJ4OMZP71v/56W/ddz/J3nvgnQ++8TSMFmFAsYtmfVK0rZs0/JdyZMsHVEN\nrIuyNEmFnHvMZ6dsozBZzmui/AT4U0FSWCXt38nPVfm4tQESyK9CU4isxUT0Wiwl\nwWmIRVFjAgMBAAECggEAAi/cJN84N7LfY3bbA90vGhTM4Ixdgo98sAtLoiIcteHC\ndtL7QwDAw9hjtSK9VRBcBbTewRV+zkOEQb3czkCvJn5WWVO5TWZpqN8FGrYOS8tJ\nvtSwu3ckI0XWfNVIqeaAdPx5Svrk+F8UZNjg6r09mBtpY3Ea+qrbPrvkI3E0wN5J\nhLPs4GLIiYM3fxfwN2S1wEy40ULYZD7s13nGHsHcPNBMBu8/q8vP4tafEdfg5M9T\nasa94HIlFdOV1xsDi87uzQ0fQuDiCACGUOP/826EaQbiTz6nPzbM4Z7jJEwPyojN\nYAEzj+Au/kCF2Ot89VQ4XJXhFnNrM1DuaFQX2EcVwQKBgQDQpWAYDQnU/OVZGpQt\n+tFhomKcossO6UobQqxIrMyQRh7AGmQAstDnjHjVCcBLBOKIRaATQcOGe3AKoGAa\ndLIBhW1uOLTLDKZwd19J8aiPtr1i1pTDIal1A0mihFeLdtSaeti9HfbcdWMw85N+\nJJMLOuGJSeaCuqU3x7c2jLDt4QKBgQDKrSJN6t+RICwdgVp5IwIkZk3OiYJrmyhe\nLcIPsfNLBsTm3byMdkCieEW8yC2g/m0bci5b8i07sCxzW2gVNZStCm9fqFVTH2YQ\ndVxApC6J2bOHRM7pP9kmgvEjDcPSVcBf/WNo40LbIHWOz44fpyZKlvZfRso1XvEg\ncvBIUC3/wwKBgQCEQF8hv5n81HR3v14vqu1vSqPhD1tQz1T25JMZ2dYsmRXCxLeg\nrbe3HZY1y2GE68q/gAqQSj4qjzGcur1P56oxRqbvl6gZkKIrZa8TyscCmV2AOe2/\npiWokvXouqRXNXJbnQSnE0g3w3ylOa/C5GIEW0U8ZYhkUvSI3+HlBNbdYQKBgH3U\nX6fgWkzPnJP06yaqBXpzyXqhBMep3LYb0o/L+zBpU3XB1H2gLSCxaxp5bVq2myaf\nDQkoocyc4/v6k/QW/+cuw1awO8kPhV5SfqPjSsFq13rzzUIi0b6HLQ6GTT0Y4Aa4\nWi+UaL2CuXmggqxEKhHFB9C/MmtQNoPe2C8NGIHhAoGBALaseYmatZwJMREUzZnG\ncBdTQ/GQ3gZtLoP8JDk3nhPyFdY0GJjlDonxdoyiiMOJuVuoZoc5J4s4RhJcUOuI\nWVAbVjfoYn+lISdGeSD3fAaAuEEQFCPu/dgnBfUw8eSxdSbKewt9HCqqaaLORbWb\nhNS6Smo1kcC9owLY8EV6dPiU\n-----END PRIVATE KEY-----\n", + "balance": 10000000 + }, + { + "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA15mQWY8xDCGAm7+LvmtW\nLZ1UHV2E6lEfLs169NLktL9cT9OqVvzKrTxYGXPMojCutg5+f4yXdyZz5RT/sbxa\nF9Hh0P3mIrYMlIGmBQr2WcIbe9YA4eXOHwta/lN0RBbrzDfBOInG3Tqjrke33UYb\nFcfIY5fVwmd/rzHoLH4XGZpAowmrkbOmul9olOItwRa2zkuaTQm+dELX2lgkyxhI\neP3+ue0ZJyQYkA4TwSZIjmrrZ9/mH82u446EwNTKgdapX/jTbWx6laGGfBNFpap8\nyqIvi1VZ6tcUUPyJrPzxsfeGAtl96b9oUvalWpeUl5XeplY7q5tIhZdS6fa1vWjf\nXQIDAQAB\n-----END PUBLIC KEY-----\n", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDXmZBZjzEMIYCb\nv4u+a1YtnVQdXYTqUR8uzXr00uS0v1xP06pW/MqtPFgZc8yiMK62Dn5/jJd3JnPl\nFP+xvFoX0eHQ/eYitgyUgaYFCvZZwht71gDh5c4fC1r+U3REFuvMN8E4icbdOqOu\nR7fdRhsVx8hjl9XCZ3+vMegsfhcZmkCjCauRs6a6X2iU4i3BFrbOS5pNCb50Qtfa\nWCTLGEh4/f657RknJBiQDhPBJkiOautn3+Yfza7jjoTA1MqB1qlf+NNtbHqVoYZ8\nE0WlqnzKoi+LVVnq1xRQ/Ims/PGx94YC2X3pv2hS9qVal5SXld6mVjurm0iFl1Lp\n9rW9aN9dAgMBAAECggEAEqa0v6KtDg0Z6J9d8fesKV8KZ03vUSv6ZByfje1unppN\nXJSFkGfnCqyFPJJxefh3LMW9OMCLW+HmNcOmyRPvwU7FV8SXM5qHJRPALoB/xk0N\n/p0JT4GHXtUnJohGrxtCg9Sw8PiuTPYlMc7/f3NwpWY2ljMM6Xf1qkzJdovj6LFw\nRYS9PkRkc5UTB+Ha2zxXQplRijmTXsFuSkv8QtHeMeH5b8nyigPj2TU7kabVl0Za\nPXL+CXmbYCmxm+bIiDrlvAGUOPwz+COB7tuTVTkbVrDvigzpAm8zjZ0Z4s3CkEVJ\nX6nM6dFxjNURNwAd3Q05+Eso67YzrDNcIU6+YBKzEQKBgQD1Q6bVYAUBFLwx5ZUS\n+JG+pCNhslXpbrP0AGPoIGnP7dPaiTU9ngP7YOUpUfwW7gC2O6o2JF7yuZcx7e80\nd18rNdc8z7HMYe1fOXAKMTwgQ8zt6XV65eUgUUXACyxrlrVxq1Ki7u+eVMAM2IuN\ncaY+ShI3csIGHMJywRluJtgA2QKBgQDhCYC9RNJDe80iRvSo8juSacC43KrGQ5YF\ntN7JSOUbPo2rMEWaeHeD+P57LiXHx+1VfUAeXMFZznsu5kuXfDZtfAx9u8KKEDyD\nzy3BewyCmb148ku9ua/H2MMGO5/r8x+B9IqlFU0HgLPlSgTDqHjNjv1/vxAmMVYA\nNhmkYz7AJQKBgD2quuHfeKKi8gahsO/KK+sBHhxMWKmOuhRZnMUakUBEjgXehdJu\nTpX/8N6IzJd/VBJ9wfhHANCKtWdKaeCJOxaEPQ//MLcw0BwnNvJ5GqMKKRaQooc6\nEngjtyPoK00J4ako6GgrJ3hp2rfgIoZ30tLKkPX8Fqiuxn1P6/L0NU9RAoGAdDYo\nGZXUz6y7PpxN9IFDalTahZzPRBva+Y9yDZhrWCpxlpoTF7ZmYezP3mTydb01PxSz\na/qSjwImcw3Ss6Qdp2SMPtIboJjJ53eyNVyKFVe3lSHrI0slD5tM+4cHCx4+gyn5\n/06EvP8l557V3wS9fcn9915/xHlt5CYMD+yAl1ECgYBcoABokPJK25VUvaJd3Xqi\nGh2RVeYydQX2IQCsZdmBz5+1ZYR9BgNomMTyHuU2not8euiAVxIWQVNqsP7NAoLX\n2BWf4nUDepSrtMju8WVkzebGHtqFQ1u7ic+4NvB8TJPKIo2/mEEJFSxpc589kW1C\nwakV1tmLzm2ldJ/Caa23yQ==\n-----END PRIVATE KEY-----\n", + "balance": 10000000 + }, + { + "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwmPppK3xhKmpTyZf1JhR\nCComwG/kiNcLUXIRF9UGr6UdH53RnQ+A5eFD7ExRfFL1+yjtZ8jsPvXLwpWFLUJm\nCbLf/JoEDg4Q2OZk796PTJk+vWiVCQBsu0hzvMz87eAR2RxZQmC8AItXRpT468Ag\n5d+SEcnEQZN9zjACfum4diZeZavUGQSxpyTCdgnpyvXo45gf8gSDnKrQIeM9eFyZ\n0N4jeLQmkRdDralWI0wawGNo/p7xIAlxJJu+yNAhu50QvYX5ImtY9YRJIAjdNLWi\nC/TiY3Mlx+XwjkrS4RGVH6YMXwVaeyCNYaJjS/P/Yi73NXkj6TH23Y3lFSdwUhJ/\nlwIDAQAB\n-----END PUBLIC KEY-----\n", + "privateKey": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDCY+mkrfGEqalP\nJl/UmFEIKibAb+SI1wtRchEX1QavpR0fndGdD4Dl4UPsTFF8UvX7KO1nyOw+9cvC\nlYUtQmYJst/8mgQODhDY5mTv3o9MmT69aJUJAGy7SHO8zPzt4BHZHFlCYLwAi1dG\nlPjrwCDl35IRycRBk33OMAJ+6bh2Jl5lq9QZBLGnJMJ2CenK9ejjmB/yBIOcqtAh\n4z14XJnQ3iN4tCaRF0OtqVYjTBrAY2j+nvEgCXEkm77I0CG7nRC9hfkia1j1hEkg\nCN00taIL9OJjcyXH5fCOStLhEZUfpgxfBVp7II1homNL8/9iLvc1eSPpMfbdjeUV\nJ3BSEn+XAgMBAAECggEARjTanhD6kQzLqtvzsW8fmQDMRSVEV02/dzdoyAA2rXpH\nRfN0ULLYPW45316C8ZFu6LqT369375dLc6gX1MxN2QPjFLK1czQOTqALZXyuqZ4R\nRExAw2MFYjCdKFuqn+kU2SJ9dFVGoTWyHDpSmqal2w//i/AgswRnY+///sE/z1+T\nwUAlYRq5nWGHBhbKorFTcnQfau1qg36gLf3nzJt5OML8b2fh3Qnqy71xIFv+lg0U\n4eq6m1jGkrrtWHzfgDI3CdGqEOZy1NI9CbKjjkjoY2OzDt93OG5cDypEspViSzac\nlnJJJQxOvP5epo1iTcq920rJE07JD0lTF9XhAGwibQKBgQDsWGZLWtWnC1iG6Dt8\nzCNnEe0ziu461zXI5M92Oo3k0nusFQRvFxaFmbOS2pXEUPDN3xzsaRfYeTDHa2x8\nRnIAdB+PBs7+tZWIH+1ehgIwzdvZ7w6FbV5iPRvi8YM+dxLtufme/uMlR34T3Ybu\nknlyMaI5QNiMbC+3LeUitwTHewKBgQDSjlMS6jwOPzQutYobRzuyvCeYqYz92OAD\nrdF2857I72RXOvkXn21hsz6JfjR25HnkFzCBn40EesDFZGf9KSM4QsX3WSE4Qj8V\nO6lga9KylPOZzKXuFMM4ZzCHVC2ZgjqeJNQDyLZOSZPyiXWS3fw0ELKe8oYzAbV1\nB8xNHZ+flQKBgB6q+dsdVkvutPYMfGudMZrWQKlHhdIsytXemu0EfBUNGu8hawnf\nPRTo9NxBcN2ggx6zrNUgbe+xChtWXSVjHrfgWZf9ki6K8XRQcESrru0WSUjmNEQs\nBOtKcqvoGteoK9SdJT70ZBUaScnaHQAoEYo31o0HVRIjl611Tx5KEVyxAoGBAIn9\nKEhvJeiYV1R9OQNjjaY3VSPhijsalMUUdwAj7N32taXNSk3G9+JdnBBzm2KHnbJz\n2CA7NinHYOp8QQJSoXhPmnnd+b1q/PinXvuH7k+G+pbC3cPH66DdT5qA+lluVYgq\nzp+kPIXWBpWerGUSAiUFBGrSukxXRFpsP+ISFfh5AoGAdCF8X+utT0IVj+gk6Kp+\n94PHDV0VyVO7+e2o4zqxDoaV8WS7Zm2TbQflCbPMBmU+xJre12p2GFZhn74waLZH\n0JlbRBDyejmf1ozQsPfaPyxDWZRf5+siWNyNzlFAPRrm1+eb1vaf6Vl/i6K9bxUC\nimxJH/IPurFt+2M9KsTZ2Sc=\n-----END PRIVATE KEY-----\n", + "balance": 10000000 + } +] \ No newline at end of file diff --git a/minerServer.js b/minerServer.js new file mode 100644 index 0000000..72b3765 --- /dev/null +++ b/minerServer.js @@ -0,0 +1,258 @@ +const WebSocket = require('ws'); +const crypto = require('crypto'); +const express = require('express'); +const net = require('net'); + +//exposing a REST API to interact with the miner + +const app = express(); +app.use(express.json()); +let PORT = 3000; + +//if 3000 already in use, try 3001 or 3002 +function checkPortInUse(PORT, callback) { + const server = net.createServer(); + server.once('error', function (err) { + if (err.code === 'EADDRINUSE') { + console.log(`Port ${PORT} is in use, trying another port`); + PORT++; + checkPortInUse(PORT, callback); + } else { + callback(err); + } + }); + + server.once('listening', function () { + server.close(); + callback(null, PORT); + }); + + server.listen(PORT); +} + +checkPortInUse(PORT, (err, availablePort) => { + if (err) { + console.error('Error checking port:', err); + } else { + PORT = availablePort; + console.log("Launching server on port", PORT); + launchServer(); + } +}); + +function launchServer() { + // Expose sendTransaction via an Express API + app.post('/sendTransaction', (req, res) => { + const { sender, recipient, amount, privateKey } = req.body; + + if (!sender || !recipient || !amount || !privateKey) { + return res.status(400).send('Missing required fields'); + } + + sendTransaction(sender, recipient, amount, privateKey); + res.send('Transaction processed'); + }); + + app.listen(PORT, () => { + console.log(`Server running on port ${PORT}`); + }); + + const ws = new WebSocket('ws://localhost:8080'); + + let blockchain = []; // Store blockchain in-memory + let transactionPool = []; // Local pool for unconfirmed transactions + let difficulty; // Difficulty will be updated by the central server + let balances = {}; // Ledger to track account balances + let canMine = false; // Flag to control mining + + ws.on('open', () => { + console.log('Connected to central server'); + // Request the current blockchain and balances from the central server + ws.send(JSON.stringify({ type: 'request_blockchain' })); + }); + + ws.on('message', (message) => { + const data = JSON.parse(message); + + if (data.type === 'block') { + handleReceivedBlock(data.block); + difficulty = data.difficulty; // Update difficulty + balances = data.balances; // Update balances + } else if (data.type === 'blockchain') { + if (data.blockchain.length > blockchain.length) { + blockchain = data.blockchain; + difficulty = data.difficulty; // Update difficulty + balances = data.balances; // Update balances + console.log('Blockchain, difficulty, and balances updated'); + } else { + console.log('Received blockchain is not longer, ignoring'); + } + } else if (data.type === 'transaction') { + console.log('Received transaction from central server, adding to pool'); + transactionPool.push(data.transaction); + } else if (data.type === 'start_mining') { + difficulty = data.difficulty; // Update difficulty + canMine = true; + console.log('Received start mining signal'); + mineBlock(); + } else if (data.type === 'block_finalized') { + canMine = false; + console.log('Mining stopped as block was finalized'); + } + }); + + function handleReceivedBlock(newBlock) { + const lastBlock = blockchain[blockchain.length - 1]; + + if (lastBlock && lastBlock.hash === newBlock.previousHash) { + blockchain.push(newBlock); + updateLocalBalances(newBlock.transactions); + console.log('New block added to the chain:', newBlock); + } else if (lastBlock && newBlock.index === blockchain.length) { + console.log('Fork detected. Comparing chains...'); + + if (validateBlock(newBlock)) { + blockchain.push(newBlock); + updateLocalBalances(newBlock.transactions); + console.log('Fork resolved: Added new block as it extends a longer chain.'); + } else { + console.log('Fork rejected: New block does not lead to a longer chain.'); + } + } else { + console.log('Invalid block rejected'); + } + } + + function validateTransaction(transaction) { + const { sender, recipient, amount, signature } = transaction; + + const verifier = crypto.createVerify('SHA256'); + verifier.update(JSON.stringify({ sender, recipient, amount })); + const isValid = verifier.verify(sender, signature, 'hex'); + + // Check if sender has enough balance + if (balances[sender] === undefined || balances[sender] < amount) { + console.log('Transaction rejected due to insufficient balance'); + return false; + } + + // Additional checks can be added here + + if (isValid) { + console.log('Transaction is valid'); + return true; + } else { + console.log('Transaction is invalid'); + return false; + } + } + + function mineBlock() { + if (!canMine || transactionPool.length === 0) { + console.log('Cannot mine: Either mining is not allowed or no transactions to mine'); + return; + } + + const previousBlock = blockchain[blockchain.length - 1]; + const previousHash = previousBlock ? previousBlock.hash : ''; + let nonce = 0; + let hash; + + // Filter out any invalid transactions before mining + const validTransactions = transactionPool.filter(validateTransaction); + + if (validTransactions.length === 0) { + console.log('No valid transactions to mine'); + return; + } + + // Simple proof-of-work algorithm with dynamic difficulty + do { + nonce++; + hash = crypto.createHash('sha256') + .update(previousHash + JSON.stringify(validTransactions) + nonce) + .digest('hex'); + } while (hash.substring(0, difficulty) !== '0'.repeat(difficulty) && canMine); + + if (!canMine) { + console.log('Mining was stopped'); + return; + } + + const block = { + index: blockchain.length, + timestamp: Date.now(), + transactions: validTransactions, // Include only valid transactions in the block + nonce, + hash, + previousHash, + }; + + ws.send(JSON.stringify({ type: 'block', block })); + canMine = false; // Stop further mining + + // Clear the transaction pool only of valid transactions that were included in the block + transactionPool = transactionPool.filter(tx => !validTransactions.includes(tx)); + updateLocalBalances(validTransactions); + } + + // Function to update local balances based on transactions in the received block + function updateLocalBalances(transactions) { + transactions.forEach(tx => { + const { sender, recipient, amount } = tx; + + // Deduct the amount from the sender's balance + if (balances[sender] !== undefined) { + balances[sender] -= amount; + } + + // Add the amount to the recipient's balance + if (balances[recipient] !== undefined) { + balances[recipient] += amount; + } else { + balances[recipient] = amount; + } + }); + + console.log('Updated local balances:', balances); + } + + // Function to handle receiving a new transaction + function handleTransaction(transaction) { + if (validateTransaction(transaction)) { + transactionPool.push(transaction); + console.log('Transaction added to local pool and broadcasting to central server'); + + // Broadcast the valid transaction to the central server + ws.send(JSON.stringify({ type: 'transaction', transaction })); + } else { + console.log('Invalid transaction rejected'); + } + } + + // Function to create and send a new transaction + function sendTransaction(sender, recipient, amount, privateKey) { + if (balances[sender] === undefined || balances[sender] < amount) { + console.log('Insufficient balance to send the transaction'); + return; + } + + const transaction = { + sender, + recipient, + amount, + timestamp: Date.now() + }; + + // Sign the transaction with the sender's private key + const sign = crypto.createSign('SHA256'); + sign.update(JSON.stringify({ sender, recipient, amount })); + const signature = sign.sign(privateKey, 'hex'); + + transaction.signature = signature; + + // Handle and broadcast the transaction + handleTransaction(transaction); + } + +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..78fa8c8 --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "ws": "^8.18.0", + "crypto": "^1.0.1", + "express": "^4.19.2" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..35ccbdb --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,567 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + crypto: + specifier: ^1.0.1 + version: 1.0.1 + express: + specifier: ^4.19.2 + version: 4.19.2 + ws: + specifier: ^8.18.0 + version: 8.18.0 + +packages: + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + crypto@1.0.1: + resolution: {integrity: sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==} + deprecated: This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in. + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + express@4.19.2: + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} + engines: {node: '>= 0.10.0'} + + finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + + serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + +snapshots: + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + array-flatten@1.1.1: {} + + body-parser@1.20.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + bytes@3.1.2: {} + + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.0.6: {} + + cookie@0.6.0: {} + + crypto@1.0.1: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + depd@2.0.0: {} + + destroy@1.2.0: {} + + ee-first@1.1.1: {} + + encodeurl@1.0.2: {} + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + + escape-html@1.0.3: {} + + etag@1.8.1: {} + + express@4.19.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.2 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.6.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + finalhandler@1.2.0: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + forwarded@0.2.0: {} + + fresh@0.5.2: {} + + function-bind@1.1.2: {} + + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + inherits@2.0.4: {} + + ipaddr.js@1.9.1: {} + + media-typer@0.3.0: {} + + merge-descriptors@1.0.1: {} + + methods@1.1.2: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@1.6.0: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + negotiator@0.6.3: {} + + object-inspect@1.13.2: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + parseurl@1.3.3: {} + + path-to-regexp@0.1.7: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + qs@6.11.0: + dependencies: + side-channel: 1.0.6 + + range-parser@1.2.1: {} + + raw-body@2.5.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + send@0.18.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serve-static@1.15.0: + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + setprototypeof@1.2.0: {} + + side-channel@1.0.6: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.2 + + statuses@2.0.1: {} + + toidentifier@1.0.1: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + unpipe@1.0.0: {} + + utils-merge@1.0.1: {} + + vary@1.1.2: {} + + ws@8.18.0: {} From af2e7bbe3626d389e18a75a8199000c92ba44e0e Mon Sep 17 00:00:00 2001 From: axatbhardwaj Date: Sat, 17 Aug 2024 01:09:05 +0530 Subject: [PATCH 2/7] draft 2 --- centralServer.js | 69 ++++++++++++++++----------- fund-wallets.txt | 20 ++++---- minerServer.js | 119 ++++++++++++++++++++++++++++++++++------------- package.json | 5 +- pnpm-lock.yaml | 53 +++++++++++++++++++++ 5 files changed, 196 insertions(+), 70 deletions(-) diff --git a/centralServer.js b/centralServer.js index 3813e29..8c356bb 100644 --- a/centralServer.js +++ b/centralServer.js @@ -1,7 +1,11 @@ +// centralServer.js + const WebSocket = require('ws'); const crypto = require('crypto'); const fs = require('fs'); +const EC = require('elliptic').ec; +const ec = new EC('secp256k1'); const wss = new WebSocket.Server({ port: 8080 }); let blockchain = []; // Store blockchain in-memory @@ -12,36 +16,43 @@ const adjustmentInterval = 5; // Adjust difficulty every 5 blocks let balances = {}; // Ledger to track account balances let miningInProgress = false; // Flag to prevent multiple blocks being mined in the same interval +// Function to create a wallet with public key, private key, and address +function createWallet() { + const keyPair = ec.genKeyPair(); + const privateKey = keyPair.getPrivate('hex'); + const publicKey = keyPair.getPublic('hex'); + const address = publicKeyToAddress(publicKey); + + return { publicKey, privateKey, address }; +} + +// Function to derive address from public key +function publicKeyToAddress(publicKey) { + const sha256 = crypto.createHash('sha256').update(publicKey, 'hex').digest(); + const ripemd160 = crypto.createHash('ripemd160').update(sha256).digest('hex'); + return ripemd160; +} + // Function to create key pairs and fund initial balances async function initializeBlockchain() { - const keypairs = []; - const initialBalance = 10000000; const wallets = []; + const initialBalance = 10000000; for (let i = 0; i < 4; i++) { - const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { - modulusLength: 2048, - publicKeyEncoding: { type: 'spki', format: 'pem' }, - privateKeyEncoding: { type: 'pkcs8', format: 'pem' } - }); - - keypairs.push({ publicKey, privateKey }); + const wallet = createWallet(); // Assign the initial balance to the wallet - balances[publicKey] = initialBalance; + balances[wallet.address] = initialBalance; - wallets.push({ publicKey: publicKey, privateKey: privateKey, balance: initialBalance }); + wallets.push({ publicKey: wallet.publicKey, privateKey: wallet.privateKey, address: wallet.address, balance: initialBalance }); } - console.log('Wallets:', wallets); - console.log('Balances:', balances); - console.log("starting the file save"); + console.log("Starting the file save"); // Save the wallet information to a file fs.writeFileSync('fund-wallets.txt', JSON.stringify(wallets, null, 2)); - const genesisBlock = { index: 0, previousHash: '0'.repeat(64), @@ -53,15 +64,13 @@ async function initializeBlockchain() { blockchain.push(genesisBlock); } - - wss.on('connection', (ws) => { miners.push(ws); console.log('New miner connected'); - //check if the blockchain is empty + // Check if the blockchain is empty if (blockchain.length === 0) { - //intilizing the blockchain + // Initializing the blockchain initializeBlockchain(); console.log('Blockchain initialized'); } @@ -76,7 +85,7 @@ wss.on('connection', (ws) => { miningInProgress = true; console.log('Block mined and accepted, stopping mining'); - // Broadcast the new block to all miners + // Broadcast the new block and updated balances to all miners miners.forEach(miner => { if (miner !== ws) { miner.send(JSON.stringify({ type: 'block', block: data.block, difficulty, balances })); @@ -85,7 +94,7 @@ wss.on('connection', (ws) => { // Notify miners to stop mining miners.forEach(miner => { - miner.send(JSON.stringify({ type: 'block_finalized', blockIndex: data.block.index })); + miner.send(JSON.stringify({ type: 'block_finalized', blockIndex: data.block.index, balances })); }); // Adjust difficulty if necessary @@ -103,6 +112,8 @@ wss.on('connection', (ws) => { } }); console.log('Transaction broadcasted to all miners'); + } else if (data.type === 'request_balances') { + ws.send(JSON.stringify({ type: 'balances', balances })); } }); @@ -142,7 +153,6 @@ function adjustDifficulty() { const lastBlock = blockchain[blockchain.length - 1]; const blocksToConsider = blockchain.slice(-adjustmentInterval); - // Calculate the average block time for the last 'adjustmentInterval' blocks const totalBlockTime = blocksToConsider.reduce((total, block, index) => { if (index > 0) { return total + (block.timestamp - blocksToConsider[index - 1].timestamp); @@ -152,7 +162,6 @@ function adjustDifficulty() { const averageBlockTime = totalBlockTime / (blocksToConsider.length - 1); - // Adjust difficulty based on the average block time if (averageBlockTime < targetBlockTime) { difficulty++; console.log('Difficulty increased to:', difficulty); @@ -165,13 +174,19 @@ function adjustDifficulty() { // Function to update balances based on the transactions in a new block function updateBalances(transactions) { transactions.forEach(tx => { - const { sender, recipient, amount } = tx; + const { publicKey, recipient, amount } = tx; + const sender = publicKeyToAddress(publicKey); + console.log("Sender:", sender); + if (sender === undefined) { + console.log('Invalid sender address'); + return; + } // Deduct the amount from the sender's balance if (balances[sender] !== undefined) { balances[sender] -= amount; } else { - balances[sender] = -amount; // Allow negative balance (overdraft) for simplicity + balances[sender] = -amount; } // Add the amount to the recipient's balance @@ -188,10 +203,10 @@ function updateBalances(transactions) { // Start mining process at regular intervals setInterval(() => { miningInProgress = false; - console.log('New mining round started'); + console.log('New mining Epoch started'); miners.forEach(miner => { miner.send(JSON.stringify({ type: 'start_mining', difficulty })); }); }, targetBlockTime); -console.log('Central server running on ws://localhost:8080'); +console.log('Central server running on ws://localhost:8080'); \ No newline at end of file diff --git a/fund-wallets.txt b/fund-wallets.txt index f90ce51..1f2aab9 100644 --- a/fund-wallets.txt +++ b/fund-wallets.txt @@ -1,22 +1,26 @@ [ { - "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4gHlkdQXC7ufn2sfhasP\ns53C7ThcTy2HWp2v65Erjd6+1AEkjOCew10w0hqx5cmjqJ9Mc3PFtxlh+YMTpRf1\nboB/TI4Jm/vyD7aYMJpAJdqI+Rj3Lu8lIIsR7UMZ7s6hsd5xLzvAddb3xnqIza6J\nXGQjdrVWCrpvh07X90TMOU+TZ6hFAmcjCao1sMEaf5jWkY3DQODDeOP2fTR90q57\n5Mrxd8MCdqtbVti1QtEy1eEIq9dKzlv9EySu0M49r4dg3sD841SCRzkCmcfRId4r\nPgZ58fJ8rrgreuiXUIdgIFGYMEwgoR8ervWnYGTRc2tjB7YnITtl5AQsWS8Gheeq\nuwIDAQAB\n-----END PUBLIC KEY-----\n", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDiAeWR1BcLu5+f\nax+Fqw+zncLtOFxPLYdana/rkSuN3r7UASSM4J7DXTDSGrHlyaOon0xzc8W3GWH5\ngxOlF/VugH9Mjgmb+/IPtpgwmkAl2oj5GPcu7yUgixHtQxnuzqGx3nEvO8B11vfG\neojNrolcZCN2tVYKum+HTtf3RMw5T5NnqEUCZyMJqjWwwRp/mNaRjcNA4MN44/Z9\nNH3SrnvkyvF3wwJ2q1tW2LVC0TLV4Qir10rOW/0TJK7Qzj2vh2DewPzjVIJHOQKZ\nx9Eh3is+Bnnx8nyuuCt66JdQh2AgUZgwTCChHx6u9adgZNFza2MHtichO2XkBCxZ\nLwaF56q7AgMBAAECggEADutCzRcRNGLncmhptXL+1NkFApAgtntSFDRqO08jGbjf\n+PJgXXUAa0i4uHiEUMpi5XZ/30kqU+AMPHfKHAZSWxUxZ2DNl+s65eL5oI+2If7k\njCj5PDMTd4FRUXZcp7Z31wbQ4Dt+BL6y2Xps6G2X6gOUPGRTsIlGY8oGbe9ahIKG\nmxxlTHd8wEZrMqHoAyTBGLZJxOmNVsrOA7C34b6t5916PEWVdgirMcMneIQadW+M\nCiUl7lr5kMvazJ7T3AmUBQeO0dYTrkiM1udD2Ewp4XsyIJWZafuJMed4BT/KOa+Z\nankS5NUHzj2qfYMmEHu/9LtDIK+ac17D1RIXtudr8QKBgQD+zlE+SM/1frCI3yeI\nrFFL0OyT/0aqs3eXsT3/GR9WRjg4DGQWhLMpKSj4MSfMnL/g66ywLTwnnm5kSZ6w\nCwMRiR+0NlRbt9NCpbrzij6+FJmxj79LjZdTzV+Wo63jhPAa+HGVQl/xKWYehaXZ\nYU89WxxEpu7wZ/WhFH1l8KjfSwKBgQDjEQfdmMVj8EXBd7yfQ9BrNtuZ4Jr1q4ad\nq5SbFORPbwNtNQ8cVVYGG3Ck+YspmKWrNz6cRrX5i90u2v/Ue5xrlYY67wu34cjh\nSow5gbpC8SAdigz3EHGnprXkI9n2LthL1SGK1U7WEKcXmmUglnmoItHGE6+PCjEX\nEZI/bZSMUQKBgCEnGP0AnkYz0KZOWqd2vDEgItfyu/AT37iV8BoT27bu9fuc6Rzg\n8Sqm11TLmIEP2AtcvqBBknUZMQXNal8/X4vVipF5DCrO1Sbzx0pOr3Et3v1yVZJv\n/sOBkjFYZJSYxGDN+IFSHIeVOls4SmbpOVUjWJ86FRakgBoQP2yXDnHXAoGAC0jY\nZDnobWwOe2vw8tVS8VEbns0bxmPM398cFoz2P8BbojzF0HmpOcp5UamZMesFKLop\nIWkeOBWsW1zE5BF0l6ApuJoSsgPFkPS8fUsmL5d+KhmnRUwK6fMODRV/Co4Pb81a\nymSJc3gGZdTle7Yeu61nUXm++tPd1rCn3QEcJdECgYBgjGwwk7gj631erQlSQGFx\n2L9BetUkaqp+/wVau0mu/rHtHxqwT3r6jMWW497gh25iz/ZSXXlBneqvheECXBQ8\nzCeeQ+B1vPSYpPRGSeaw0rv8Qja+p8UYC14nwW26YpWRlNyLAhfUWDF8hzcybC5J\nZLcKrRIpHCwv1h3YMb7n1A==\n-----END PRIVATE KEY-----\n", + "publicKey": "04268bdfac17f95c829704682e9c1c9a0138c669594fbff50c06f570fe62548f86f28e463c86fee6ae69eb399cdf7a2ee991b46a75d134709a85433665c1048ac3", + "privateKey": "2d8b6fa8a40444c886ccbf4c1ba4fc0b8382c46ef23805dac52d629108b9df72", + "address": "35fe84406825322eadef114a2aa7cc2c56aeaa01", "balance": 10000000 }, { - "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApS+ZimJ5CthLCmYHr86F\nVwJZG5vmHNCrYXU77A8ASxR1y+ieVfXmaMlI7rltApB/rN047hNwu/CdVbJFEwcA\n1tZzXJEmlsgn6iuoyMaRHhH1zo+n7RRwCs+j8o6nJdCBRmWT2kojx13BO3uNzbE5\nJPsIN8+KTwX5MQR8gjdUTmbSl0mHAb6YQpCIG7df0E5nJ87w6+ItPh+XwBPxQulC\neDjGT+9b/+elv3Xc/yd574J0PvvE0jBZhQLGLZn1StK2bNPyXcmTLB1RDayLsjRJ\nhZx7zGenbKMwWc5rovwE+FNBUlgl7d/Jz1X5uLUBEsivQlOIrMVE9FosJcFpiEVR\nYwIDAQAB\n-----END PUBLIC KEY-----\n", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQClL5mKYnkK2EsK\nZgevzoVXAlkbm+Yc0KthdTvsDwBLFHXL6J5V9eZoyUjuuW0CkH+s3TjuE3C78J1V\nskUTBwDW1nNckSaWyCfqK6jIxpEeEfXOj6ftFHAKz6Pyjqcl0IFGZZPaSiPHXcE7\ne43NsTkk+wg3z4pPBfkxBHyCN1ROZtKXSYcBvphCkIgbt1/QTmcnzvDr4i0+H5fA\nE/FC6UJ4OMZP71v/56W/ddz/J3nvgnQ++8TSMFmFAsYtmfVK0rZs0/JdyZMsHVEN\nrIuyNEmFnHvMZ6dsozBZzmui/AT4U0FSWCXt38nPVfm4tQESyK9CU4isxUT0Wiwl\nwWmIRVFjAgMBAAECggEAAi/cJN84N7LfY3bbA90vGhTM4Ixdgo98sAtLoiIcteHC\ndtL7QwDAw9hjtSK9VRBcBbTewRV+zkOEQb3czkCvJn5WWVO5TWZpqN8FGrYOS8tJ\nvtSwu3ckI0XWfNVIqeaAdPx5Svrk+F8UZNjg6r09mBtpY3Ea+qrbPrvkI3E0wN5J\nhLPs4GLIiYM3fxfwN2S1wEy40ULYZD7s13nGHsHcPNBMBu8/q8vP4tafEdfg5M9T\nasa94HIlFdOV1xsDi87uzQ0fQuDiCACGUOP/826EaQbiTz6nPzbM4Z7jJEwPyojN\nYAEzj+Au/kCF2Ot89VQ4XJXhFnNrM1DuaFQX2EcVwQKBgQDQpWAYDQnU/OVZGpQt\n+tFhomKcossO6UobQqxIrMyQRh7AGmQAstDnjHjVCcBLBOKIRaATQcOGe3AKoGAa\ndLIBhW1uOLTLDKZwd19J8aiPtr1i1pTDIal1A0mihFeLdtSaeti9HfbcdWMw85N+\nJJMLOuGJSeaCuqU3x7c2jLDt4QKBgQDKrSJN6t+RICwdgVp5IwIkZk3OiYJrmyhe\nLcIPsfNLBsTm3byMdkCieEW8yC2g/m0bci5b8i07sCxzW2gVNZStCm9fqFVTH2YQ\ndVxApC6J2bOHRM7pP9kmgvEjDcPSVcBf/WNo40LbIHWOz44fpyZKlvZfRso1XvEg\ncvBIUC3/wwKBgQCEQF8hv5n81HR3v14vqu1vSqPhD1tQz1T25JMZ2dYsmRXCxLeg\nrbe3HZY1y2GE68q/gAqQSj4qjzGcur1P56oxRqbvl6gZkKIrZa8TyscCmV2AOe2/\npiWokvXouqRXNXJbnQSnE0g3w3ylOa/C5GIEW0U8ZYhkUvSI3+HlBNbdYQKBgH3U\nX6fgWkzPnJP06yaqBXpzyXqhBMep3LYb0o/L+zBpU3XB1H2gLSCxaxp5bVq2myaf\nDQkoocyc4/v6k/QW/+cuw1awO8kPhV5SfqPjSsFq13rzzUIi0b6HLQ6GTT0Y4Aa4\nWi+UaL2CuXmggqxEKhHFB9C/MmtQNoPe2C8NGIHhAoGBALaseYmatZwJMREUzZnG\ncBdTQ/GQ3gZtLoP8JDk3nhPyFdY0GJjlDonxdoyiiMOJuVuoZoc5J4s4RhJcUOuI\nWVAbVjfoYn+lISdGeSD3fAaAuEEQFCPu/dgnBfUw8eSxdSbKewt9HCqqaaLORbWb\nhNS6Smo1kcC9owLY8EV6dPiU\n-----END PRIVATE KEY-----\n", + "publicKey": "04d7ceef69212b6b650e52228640a14a30e25c3848bad0706718e9805717c987ee42f5121e676d4d837b0542748acbf721e1549b3c1e15d9855d29cf51b0a0013f", + "privateKey": "dae05a60fc539472d4b430f532cd2ebbd61ee98aa9030b8e151de51f0c8c9d0d", + "address": "80403d5aade3bf55341cabbb7f5605ed406ad95b", "balance": 10000000 }, { - "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA15mQWY8xDCGAm7+LvmtW\nLZ1UHV2E6lEfLs169NLktL9cT9OqVvzKrTxYGXPMojCutg5+f4yXdyZz5RT/sbxa\nF9Hh0P3mIrYMlIGmBQr2WcIbe9YA4eXOHwta/lN0RBbrzDfBOInG3Tqjrke33UYb\nFcfIY5fVwmd/rzHoLH4XGZpAowmrkbOmul9olOItwRa2zkuaTQm+dELX2lgkyxhI\neP3+ue0ZJyQYkA4TwSZIjmrrZ9/mH82u446EwNTKgdapX/jTbWx6laGGfBNFpap8\nyqIvi1VZ6tcUUPyJrPzxsfeGAtl96b9oUvalWpeUl5XeplY7q5tIhZdS6fa1vWjf\nXQIDAQAB\n-----END PUBLIC KEY-----\n", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDXmZBZjzEMIYCb\nv4u+a1YtnVQdXYTqUR8uzXr00uS0v1xP06pW/MqtPFgZc8yiMK62Dn5/jJd3JnPl\nFP+xvFoX0eHQ/eYitgyUgaYFCvZZwht71gDh5c4fC1r+U3REFuvMN8E4icbdOqOu\nR7fdRhsVx8hjl9XCZ3+vMegsfhcZmkCjCauRs6a6X2iU4i3BFrbOS5pNCb50Qtfa\nWCTLGEh4/f657RknJBiQDhPBJkiOautn3+Yfza7jjoTA1MqB1qlf+NNtbHqVoYZ8\nE0WlqnzKoi+LVVnq1xRQ/Ims/PGx94YC2X3pv2hS9qVal5SXld6mVjurm0iFl1Lp\n9rW9aN9dAgMBAAECggEAEqa0v6KtDg0Z6J9d8fesKV8KZ03vUSv6ZByfje1unppN\nXJSFkGfnCqyFPJJxefh3LMW9OMCLW+HmNcOmyRPvwU7FV8SXM5qHJRPALoB/xk0N\n/p0JT4GHXtUnJohGrxtCg9Sw8PiuTPYlMc7/f3NwpWY2ljMM6Xf1qkzJdovj6LFw\nRYS9PkRkc5UTB+Ha2zxXQplRijmTXsFuSkv8QtHeMeH5b8nyigPj2TU7kabVl0Za\nPXL+CXmbYCmxm+bIiDrlvAGUOPwz+COB7tuTVTkbVrDvigzpAm8zjZ0Z4s3CkEVJ\nX6nM6dFxjNURNwAd3Q05+Eso67YzrDNcIU6+YBKzEQKBgQD1Q6bVYAUBFLwx5ZUS\n+JG+pCNhslXpbrP0AGPoIGnP7dPaiTU9ngP7YOUpUfwW7gC2O6o2JF7yuZcx7e80\nd18rNdc8z7HMYe1fOXAKMTwgQ8zt6XV65eUgUUXACyxrlrVxq1Ki7u+eVMAM2IuN\ncaY+ShI3csIGHMJywRluJtgA2QKBgQDhCYC9RNJDe80iRvSo8juSacC43KrGQ5YF\ntN7JSOUbPo2rMEWaeHeD+P57LiXHx+1VfUAeXMFZznsu5kuXfDZtfAx9u8KKEDyD\nzy3BewyCmb148ku9ua/H2MMGO5/r8x+B9IqlFU0HgLPlSgTDqHjNjv1/vxAmMVYA\nNhmkYz7AJQKBgD2quuHfeKKi8gahsO/KK+sBHhxMWKmOuhRZnMUakUBEjgXehdJu\nTpX/8N6IzJd/VBJ9wfhHANCKtWdKaeCJOxaEPQ//MLcw0BwnNvJ5GqMKKRaQooc6\nEngjtyPoK00J4ako6GgrJ3hp2rfgIoZ30tLKkPX8Fqiuxn1P6/L0NU9RAoGAdDYo\nGZXUz6y7PpxN9IFDalTahZzPRBva+Y9yDZhrWCpxlpoTF7ZmYezP3mTydb01PxSz\na/qSjwImcw3Ss6Qdp2SMPtIboJjJ53eyNVyKFVe3lSHrI0slD5tM+4cHCx4+gyn5\n/06EvP8l557V3wS9fcn9915/xHlt5CYMD+yAl1ECgYBcoABokPJK25VUvaJd3Xqi\nGh2RVeYydQX2IQCsZdmBz5+1ZYR9BgNomMTyHuU2not8euiAVxIWQVNqsP7NAoLX\n2BWf4nUDepSrtMju8WVkzebGHtqFQ1u7ic+4NvB8TJPKIo2/mEEJFSxpc589kW1C\nwakV1tmLzm2ldJ/Caa23yQ==\n-----END PRIVATE KEY-----\n", + "publicKey": "04d03526d3c02fa12b997f1adbcdf4c7e7c6872b7f75abc45c60cdd8901e2d24d1ff926d665552ba90ff2daa32d7f77750116d7614b923be652cd6206cc21298f5", + "privateKey": "06501429ea1a1d85dd8c7815f4bae89983ba52c95aae1d71eebaa50c1170397d", + "address": "4502b20338b776eec86dc49e7a74dcad366eaf61", "balance": 10000000 }, { - "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwmPppK3xhKmpTyZf1JhR\nCComwG/kiNcLUXIRF9UGr6UdH53RnQ+A5eFD7ExRfFL1+yjtZ8jsPvXLwpWFLUJm\nCbLf/JoEDg4Q2OZk796PTJk+vWiVCQBsu0hzvMz87eAR2RxZQmC8AItXRpT468Ag\n5d+SEcnEQZN9zjACfum4diZeZavUGQSxpyTCdgnpyvXo45gf8gSDnKrQIeM9eFyZ\n0N4jeLQmkRdDralWI0wawGNo/p7xIAlxJJu+yNAhu50QvYX5ImtY9YRJIAjdNLWi\nC/TiY3Mlx+XwjkrS4RGVH6YMXwVaeyCNYaJjS/P/Yi73NXkj6TH23Y3lFSdwUhJ/\nlwIDAQAB\n-----END PUBLIC KEY-----\n", - "privateKey": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDCY+mkrfGEqalP\nJl/UmFEIKibAb+SI1wtRchEX1QavpR0fndGdD4Dl4UPsTFF8UvX7KO1nyOw+9cvC\nlYUtQmYJst/8mgQODhDY5mTv3o9MmT69aJUJAGy7SHO8zPzt4BHZHFlCYLwAi1dG\nlPjrwCDl35IRycRBk33OMAJ+6bh2Jl5lq9QZBLGnJMJ2CenK9ejjmB/yBIOcqtAh\n4z14XJnQ3iN4tCaRF0OtqVYjTBrAY2j+nvEgCXEkm77I0CG7nRC9hfkia1j1hEkg\nCN00taIL9OJjcyXH5fCOStLhEZUfpgxfBVp7II1homNL8/9iLvc1eSPpMfbdjeUV\nJ3BSEn+XAgMBAAECggEARjTanhD6kQzLqtvzsW8fmQDMRSVEV02/dzdoyAA2rXpH\nRfN0ULLYPW45316C8ZFu6LqT369375dLc6gX1MxN2QPjFLK1czQOTqALZXyuqZ4R\nRExAw2MFYjCdKFuqn+kU2SJ9dFVGoTWyHDpSmqal2w//i/AgswRnY+///sE/z1+T\nwUAlYRq5nWGHBhbKorFTcnQfau1qg36gLf3nzJt5OML8b2fh3Qnqy71xIFv+lg0U\n4eq6m1jGkrrtWHzfgDI3CdGqEOZy1NI9CbKjjkjoY2OzDt93OG5cDypEspViSzac\nlnJJJQxOvP5epo1iTcq920rJE07JD0lTF9XhAGwibQKBgQDsWGZLWtWnC1iG6Dt8\nzCNnEe0ziu461zXI5M92Oo3k0nusFQRvFxaFmbOS2pXEUPDN3xzsaRfYeTDHa2x8\nRnIAdB+PBs7+tZWIH+1ehgIwzdvZ7w6FbV5iPRvi8YM+dxLtufme/uMlR34T3Ybu\nknlyMaI5QNiMbC+3LeUitwTHewKBgQDSjlMS6jwOPzQutYobRzuyvCeYqYz92OAD\nrdF2857I72RXOvkXn21hsz6JfjR25HnkFzCBn40EesDFZGf9KSM4QsX3WSE4Qj8V\nO6lga9KylPOZzKXuFMM4ZzCHVC2ZgjqeJNQDyLZOSZPyiXWS3fw0ELKe8oYzAbV1\nB8xNHZ+flQKBgB6q+dsdVkvutPYMfGudMZrWQKlHhdIsytXemu0EfBUNGu8hawnf\nPRTo9NxBcN2ggx6zrNUgbe+xChtWXSVjHrfgWZf9ki6K8XRQcESrru0WSUjmNEQs\nBOtKcqvoGteoK9SdJT70ZBUaScnaHQAoEYo31o0HVRIjl611Tx5KEVyxAoGBAIn9\nKEhvJeiYV1R9OQNjjaY3VSPhijsalMUUdwAj7N32taXNSk3G9+JdnBBzm2KHnbJz\n2CA7NinHYOp8QQJSoXhPmnnd+b1q/PinXvuH7k+G+pbC3cPH66DdT5qA+lluVYgq\nzp+kPIXWBpWerGUSAiUFBGrSukxXRFpsP+ISFfh5AoGAdCF8X+utT0IVj+gk6Kp+\n94PHDV0VyVO7+e2o4zqxDoaV8WS7Zm2TbQflCbPMBmU+xJre12p2GFZhn74waLZH\n0JlbRBDyejmf1ozQsPfaPyxDWZRf5+siWNyNzlFAPRrm1+eb1vaf6Vl/i6K9bxUC\nimxJH/IPurFt+2M9KsTZ2Sc=\n-----END PRIVATE KEY-----\n", + "publicKey": "04c67fa6b35e62d238f78002a24863d9812c7018a82cab78574afe3a2f29aa8b1c3114b6bf02ec1d2e626938d47289b43842bea199d075ee734b107bca09ce5d30", + "privateKey": "7de779f16f4aee969e4cb16cd353012cf5b85e6937a06f542e919606e382dc0d", + "address": "9e7f08cda246e8f89324ded2b782575a74c37151", "balance": 10000000 } ] \ No newline at end of file diff --git a/minerServer.js b/minerServer.js index 72b3765..c9a6bf4 100644 --- a/minerServer.js +++ b/minerServer.js @@ -1,15 +1,17 @@ +// minerserver.js const WebSocket = require('ws'); const crypto = require('crypto'); const express = require('express'); +const EC = require('elliptic').ec; const net = require('net'); -//exposing a REST API to interact with the miner - const app = express(); app.use(express.json()); let PORT = 3000; -//if 3000 already in use, try 3001 or 3002 +const ec = new EC('secp256k1'); + +// If 3000 already in use, try 3001 or 3002 function checkPortInUse(PORT, callback) { const server = net.createServer(); server.once('error', function (err) { @@ -43,16 +45,37 @@ checkPortInUse(PORT, (err, availablePort) => { function launchServer() { // Expose sendTransaction via an Express API app.post('/sendTransaction', (req, res) => { - const { sender, recipient, amount, privateKey } = req.body; - - if (!sender || !recipient || !amount || !privateKey) { - return res.status(400).send('Missing required fields'); + const { recipient, amount, privateKey } = req.body; + if (amount <= 0) { + return res.status(400).send('Amount must be greater than 0'); } - - sendTransaction(sender, recipient, amount, privateKey); + //making sure recipient is a valid address + if (recipient.length !== 40) { + return res.status(400).send('Invalid recipient address'); + } + if (privateKey.length !== 64) { + return res.status(400).send('Invalid private key'); + } + sendTransaction(recipient, amount, privateKey); res.send('Transaction processed'); }); + // Expose getBalances via an Express API + app.get('/getBalances', (req, res) => { + res.send(balances); + }); + + //expose blockheight via an Express API + app.get('/getBlockHeight', (req, res) => { + res.send({ blockHeight: blockchain.length }); + }); + + // expose getbalance of a specific address via an Express API + app.get('/getBalance/:address', (req, res) => { + const { address } = req.params; + res.send({ balance: balances[address] || 0 }); + }); + app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); }); @@ -97,7 +120,11 @@ function launchServer() { mineBlock(); } else if (data.type === 'block_finalized') { canMine = false; - console.log('Mining stopped as block was finalized'); + balances = data.balances; // Sync balances after block is finalized + console.log('Mining stopped as block was finalized and balances synced'); + } else if (data.type === 'balances') { + balances = data.balances; + console.log('Balances synced:', balances); } }); @@ -105,6 +132,11 @@ function launchServer() { const lastBlock = blockchain[blockchain.length - 1]; if (lastBlock && lastBlock.hash === newBlock.previousHash) { + if (newBlock.index === blockchain.length) { + console.log('Block already added locally, skipping duplicate processing'); + return; + } + blockchain.push(newBlock); updateLocalBalances(newBlock.transactions); console.log('New block added to the chain:', newBlock); @@ -123,21 +155,27 @@ function launchServer() { } } + + function publicKeyToAddress(publicKey) { + const sha256 = crypto.createHash('sha256').update(publicKey, 'hex').digest(); + const ripemd160 = crypto.createHash('ripemd160').update(sha256).digest('hex'); + return ripemd160; + } + function validateTransaction(transaction) { - const { sender, recipient, amount, signature } = transaction; + const { publicKey, recipient, amount, timestamp, signature } = transaction; - const verifier = crypto.createVerify('SHA256'); - verifier.update(JSON.stringify({ sender, recipient, amount })); - const isValid = verifier.verify(sender, signature, 'hex'); + const key = ec.keyFromPublic(publicKey, 'hex'); + const isValid = key.verify(crypto.createHash('sha256').update(publicKey + recipient + amount + timestamp).digest(), signature); + + // for managing balances this has nothing to do with signature verification + const senderAddress = publicKeyToAddress(publicKey); // Check if sender has enough balance - if (balances[sender] === undefined || balances[sender] < amount) { + if (balances[senderAddress] === undefined || balances[senderAddress] < amount) { console.log('Transaction rejected due to insufficient balance'); return false; } - - // Additional checks can be added here - if (isValid) { console.log('Transaction is valid'); return true; @@ -158,7 +196,6 @@ function launchServer() { let nonce = 0; let hash; - // Filter out any invalid transactions before mining const validTransactions = transactionPool.filter(validateTransaction); if (validTransactions.length === 0) { @@ -166,7 +203,6 @@ function launchServer() { return; } - // Simple proof-of-work algorithm with dynamic difficulty do { nonce++; hash = crypto.createHash('sha256') @@ -182,24 +218,37 @@ function launchServer() { const block = { index: blockchain.length, timestamp: Date.now(), - transactions: validTransactions, // Include only valid transactions in the block + transactions: validTransactions, nonce, hash, previousHash, }; - ws.send(JSON.stringify({ type: 'block', block })); - canMine = false; // Stop further mining + console.log(`Block verified: ${JSON.stringify(block)}`); - // Clear the transaction pool only of valid transactions that were included in the block + // Clear the transaction pool immediately after mining transactionPool = transactionPool.filter(tx => !validTransactions.includes(tx)); + + // Directly update balances before sending the block updateLocalBalances(validTransactions); + + ws.send(JSON.stringify({ type: 'block', block })); + console.log('Block sent to central server'); + canMine = false; } + // Function to update local balances based on transactions in the received block function updateLocalBalances(transactions) { transactions.forEach(tx => { - const { sender, recipient, amount } = tx; + const { publicKey, recipient, amount } = tx; + + const sender = publicKeyToAddress(publicKey); + console.log("Sender:", sender); + if (sender === undefined) { + console.log('Invalid sender address'); + return; + } // Deduct the amount from the sender's balance if (balances[sender] !== undefined) { @@ -231,28 +280,32 @@ function launchServer() { } // Function to create and send a new transaction - function sendTransaction(sender, recipient, amount, privateKey) { - if (balances[sender] === undefined || balances[sender] < amount) { + function sendTransaction(recipient, amount, privateKey) { + // Derive the public key from the private key + const key = ec.keyFromPrivate(privateKey, 'hex'); + const publicKey = key.getPublic().encode('hex'); + const senderAddress = publicKeyToAddress(publicKey); + + if (balances[senderAddress] === undefined || balances[senderAddress] < amount) { console.log('Insufficient balance to send the transaction'); return; } + const transaction = { - sender, + publicKey, // Use the public key as the sender recipient, amount, timestamp: Date.now() }; - // Sign the transaction with the sender's private key - const sign = crypto.createSign('SHA256'); - sign.update(JSON.stringify({ sender, recipient, amount })); - const signature = sign.sign(privateKey, 'hex'); + const transactionData = publicKey + recipient + amount + transaction.timestamp; + const signature = key.sign(crypto.createHash('sha256').update(transactionData).digest()).toDER('hex'); transaction.signature = signature; // Handle and broadcast the transaction handleTransaction(transaction); } +} -} \ No newline at end of file diff --git a/package.json b/package.json index 78fa8c8..3434a8f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "dependencies": { - "ws": "^8.18.0", "crypto": "^1.0.1", - "express": "^4.19.2" + "elliptic": "^6.5.7", + "express": "^4.19.2", + "ws": "^8.18.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 35ccbdb..4f8a64a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: crypto: specifier: ^1.0.1 version: 1.0.1 + elliptic: + specifier: ^6.5.7 + version: 6.5.7 express: specifier: ^4.19.2 version: 4.19.2 @@ -27,10 +30,16 @@ packages: array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + bn.js@4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + body-parser@1.20.2: resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -81,6 +90,9 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + elliptic@6.5.7: + resolution: {integrity: sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==} + encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} @@ -137,10 +149,16 @@ packages: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -180,6 +198,12 @@ packages: engines: {node: '>=4'} hasBin: true + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -291,6 +315,8 @@ snapshots: array-flatten@1.1.1: {} + bn.js@4.12.0: {} + body-parser@1.20.2: dependencies: bytes: 3.1.2 @@ -308,6 +334,8 @@ snapshots: transitivePeerDependencies: - supports-color + brorand@1.1.0: {} + bytes@3.1.2: {} call-bind@1.0.7: @@ -346,6 +374,16 @@ snapshots: ee-first@1.1.1: {} + elliptic@6.5.7: + dependencies: + bn.js: 4.12.0 + brorand: 1.1.0 + hash.js: 1.1.7 + hmac-drbg: 1.0.1 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + encodeurl@1.0.2: {} es-define-property@1.0.0: @@ -432,10 +470,21 @@ snapshots: has-symbols@1.0.3: {} + hash.js@1.1.7: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + hasown@2.0.2: dependencies: function-bind: 1.1.2 + hmac-drbg@1.0.1: + dependencies: + hash.js: 1.1.7 + minimalistic-assert: 1.0.1 + minimalistic-crypto-utils: 1.0.1 + http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -466,6 +515,10 @@ snapshots: mime@1.6.0: {} + minimalistic-assert@1.0.1: {} + + minimalistic-crypto-utils@1.0.1: {} + ms@2.0.0: {} ms@2.1.3: {} From f85ca1157e8dd1f1d83a503f61f7a763b9baf91c Mon Sep 17 00:00:00 2001 From: axatbhardwaj Date: Sat, 17 Aug 2024 16:38:06 +0530 Subject: [PATCH 3/7] final working prototype --- blockchainUtils.js | 59 ++++++++++++ centralServer.js | 127 +++++++++++++------------ fund-wallets.txt | 24 ++--- minerServer.js | 226 ++++++++++++++++++--------------------------- 4 files changed, 225 insertions(+), 211 deletions(-) create mode 100644 blockchainUtils.js diff --git a/blockchainUtils.js b/blockchainUtils.js new file mode 100644 index 0000000..4f15afc --- /dev/null +++ b/blockchainUtils.js @@ -0,0 +1,59 @@ +// blockchainUtils.js +const crypto = require('crypto'); +const EC = require('elliptic').ec; +const ec = new EC('secp256k1'); + +function generateBlockHash(block) { + return crypto.createHash('sha256') + .update(block.previousHash + JSON.stringify(block.transactions) + block.nonce + block.timestamp + block.index) + .digest('hex'); +} + +function publicKeyToAddress(publicKey) { + const sha256 = crypto.createHash('sha256').update(publicKey, 'hex').digest(); + const ripemd160 = crypto.createHash('ripemd160').update(sha256).digest('hex'); + return ripemd160; +} + +function createTransaction(publicKey, recipient, amount, privateKey) { + const key = ec.keyFromPrivate(privateKey, 'hex'); + const transaction = { + publicKey, + recipient, + amount, + timestamp: Date.now(), + }; + + const transactionData = publicKey + recipient + amount + transaction.timestamp; + const transactionHash = crypto.createHash('sha256').update(transactionData).digest('hex'); + const signature = key.sign(crypto.createHash('sha256').update(transactionData).digest()).toDER('hex'); + + transaction.hash = transactionHash; + transaction.signature = signature; + return transaction; +} + +function validateTransaction(transaction, balances) { + const { publicKey, recipient, amount, timestamp, signature } = transaction; + const key = ec.keyFromPublic(publicKey, 'hex'); + const isValid = key.verify(crypto.createHash('sha256').update(publicKey + recipient + amount + timestamp).digest(), signature); + + const senderAddress = publicKeyToAddress(publicKey); + if (balances[senderAddress] === undefined || balances[senderAddress] < amount) { + return false; + } + return isValid; +} + +function validateBlock(block, difficulty) { + const hash = generateBlockHash(block); + return hash.substring(0, difficulty) === '0'.repeat(difficulty); +} + +module.exports = { + generateBlockHash, + publicKeyToAddress, + createTransaction, + validateTransaction, + validateBlock, +}; diff --git a/centralServer.js b/centralServer.js index 8c356bb..4cf2b62 100644 --- a/centralServer.js +++ b/centralServer.js @@ -1,8 +1,7 @@ -// centralServer.js - +// centralServer.js const WebSocket = require('ws'); -const crypto = require('crypto'); const fs = require('fs'); +const { generateBlockHash, publicKeyToAddress, validateBlock } = require('./blockchainUtils'); const EC = require('elliptic').ec; const ec = new EC('secp256k1'); @@ -11,12 +10,11 @@ const wss = new WebSocket.Server({ port: 8080 }); let blockchain = []; // Store blockchain in-memory let miners = []; let difficulty = 4; // Initial difficulty: requires hash to start with '0000' -const targetBlockTime = 30000; // Target block time: 30 seconds const adjustmentInterval = 5; // Adjust difficulty every 5 blocks let balances = {}; // Ledger to track account balances -let miningInProgress = false; // Flag to prevent multiple blocks being mined in the same interval +const desiredBlockTime = 30000; // 30 seconds +let transactionPool = []; // Central server's transaction pool -// Function to create a wallet with public key, private key, and address function createWallet() { const keyPair = ec.genKeyPair(); const privateKey = keyPair.getPrivate('hex'); @@ -26,39 +24,29 @@ function createWallet() { return { publicKey, privateKey, address }; } -// Function to derive address from public key -function publicKeyToAddress(publicKey) { - const sha256 = crypto.createHash('sha256').update(publicKey, 'hex').digest(); - const ripemd160 = crypto.createHash('ripemd160').update(sha256).digest('hex'); - return ripemd160; -} - -// Function to create key pairs and fund initial balances async function initializeBlockchain() { const wallets = []; const initialBalance = 10000000; for (let i = 0; i < 4; i++) { const wallet = createWallet(); - - // Assign the initial balance to the wallet balances[wallet.address] = initialBalance; wallets.push({ publicKey: wallet.publicKey, privateKey: wallet.privateKey, address: wallet.address, balance: initialBalance }); } console.log('Balances:', balances); - console.log("Starting the file save"); - // Save the wallet information to a file fs.writeFileSync('fund-wallets.txt', JSON.stringify(wallets, null, 2)); + const genesisTimestamp = Date.now(); const genesisBlock = { index: 0, previousHash: '0'.repeat(64), - timestamp: Date.now(), + timestamp: genesisTimestamp, transactions: [], - nonce: 0 + nonce: 0, + hash: generateBlockHash({ index: 0, previousHash: '0'.repeat(64), timestamp: genesisTimestamp, transactions: [], nonce: 0 }) }; blockchain.push(genesisBlock); @@ -68,9 +56,7 @@ wss.on('connection', (ws) => { miners.push(ws); console.log('New miner connected'); - // Check if the blockchain is empty if (blockchain.length === 0) { - // Initializing the blockchain initializeBlockchain(); console.log('Blockchain initialized'); } @@ -81,39 +67,33 @@ wss.on('connection', (ws) => { if (data.type === 'request_blockchain') { ws.send(JSON.stringify({ type: 'blockchain', blockchain, difficulty, balances })); } else if (data.type === 'block') { - if (!miningInProgress && validateAndAddBlock(data.block)) { - miningInProgress = true; - console.log('Block mined and accepted, stopping mining'); + if (validateAndAddBlock(data.block)) { + console.log('Block mined and accepted, broadcasting to other miners'); - // Broadcast the new block and updated balances to all miners - miners.forEach(miner => { - if (miner !== ws) { - miner.send(JSON.stringify({ type: 'block', block: data.block, difficulty, balances })); - } - }); + // Notify the miner that its block was accepted + ws.send(JSON.stringify({ type: 'block_accepted', block: data.block })); - // Notify miners to stop mining + // Broadcast the accepted block to all other miners miners.forEach(miner => { - miner.send(JSON.stringify({ type: 'block_finalized', blockIndex: data.block.index, balances })); + miner.send(JSON.stringify({ type: 'block', block: data.block, difficulty, balances })); }); - // Adjust difficulty if necessary if (blockchain.length % adjustmentInterval === 0) { adjustDifficulty(); } } else { - console.log('Block rejected due to mining already in progress or validation failure'); + console.log('Block rejected due to validation failure'); + + // Notify the miner that its block was rejected + ws.send(JSON.stringify({ type: 'block_rejected', block: data.block })); } } else if (data.type === 'transaction') { - // Broadcast the transaction to all miners miners.forEach(miner => { if (miner !== ws) { miner.send(JSON.stringify({ type: 'transaction', transaction: data.transaction })); } }); console.log('Transaction broadcasted to all miners'); - } else if (data.type === 'request_balances') { - ws.send(JSON.stringify({ type: 'balances', balances })); } }); @@ -123,34 +103,64 @@ wss.on('connection', (ws) => { }); }); -// Function to validate and add a block to the blockchain function validateAndAddBlock(newBlock) { const lastBlock = blockchain[blockchain.length - 1]; + console.log('Validating new block:', newBlock); + + if (lastBlock) { + console.log('Last block:', lastBlock); + } + + if (lastBlock && lastBlock.hash === newBlock.previousHash && + newBlock.index === lastBlock.index + 1 && + validateBlock(newBlock, difficulty)) { - if (lastBlock && lastBlock.hash === newBlock.previousHash && validateBlock(newBlock)) { blockchain.push(newBlock); updateBalances(newBlock.transactions); + console.log('Block added to blockchain:', newBlock); + + // Remove transactions included in the new block from the transaction pool + removeMinedTransactions(newBlock.transactions); + + // Broadcast the updated transaction pool to all miners + broadcastTransactionPool(); + return true; } else if (!lastBlock && newBlock.index === 0) { - // Genesis block: the first block in the chain blockchain.push(newBlock); updateBalances(newBlock.transactions); + console.log('Genesis block added to blockchain:', newBlock); return true; + } else { + console.error('Block validation failed'); + if (lastBlock.hash !== newBlock.previousHash) { + console.error('Previous hash mismatch'); + } + if (newBlock.index !== lastBlock.index + 1) { + console.error('Index mismatch'); + } + if (!validateBlock(newBlock, difficulty)) { + console.error('Block does not satisfy the difficulty requirement'); + } } return false; } -// Function to validate a block based on current difficulty -function validateBlock(block) { - const hash = crypto.createHash('sha256') - .update(block.previousHash + JSON.stringify(block.transactions) + block.nonce) - .digest('hex'); - return hash.substring(0, difficulty) === '0'.repeat(difficulty); +function removeMinedTransactions(minedTransactions) { + transactionPool = transactionPool.filter(tx => { + return !minedTransactions.some(minedTx => minedTx.hash === tx.hash); + }); + console.log('Updated transaction pool after mining:', transactionPool); +} + +function broadcastTransactionPool() { + miners.forEach(miner => { + miner.send(JSON.stringify({ type: 'transactionPool', transactionPool })); + }); + console.log('Broadcasted updated transaction pool to all miners'); } -// Function to adjust the difficulty function adjustDifficulty() { - const lastBlock = blockchain[blockchain.length - 1]; const blocksToConsider = blockchain.slice(-adjustmentInterval); const totalBlockTime = blocksToConsider.reduce((total, block, index) => { @@ -162,34 +172,30 @@ function adjustDifficulty() { const averageBlockTime = totalBlockTime / (blocksToConsider.length - 1); - if (averageBlockTime < targetBlockTime) { + if (averageBlockTime < desiredBlockTime) { // 30 seconds in milliseconds difficulty++; console.log('Difficulty increased to:', difficulty); - } else if (averageBlockTime > targetBlockTime && difficulty > 1) { + } else if (averageBlockTime > desiredBlockTime && difficulty > 1) { difficulty--; console.log('Difficulty decreased to:', difficulty); } } -// Function to update balances based on the transactions in a new block function updateBalances(transactions) { transactions.forEach(tx => { const { publicKey, recipient, amount } = tx; const sender = publicKeyToAddress(publicKey); - console.log("Sender:", sender); if (sender === undefined) { console.log('Invalid sender address'); return; } - // Deduct the amount from the sender's balance if (balances[sender] !== undefined) { balances[sender] -= amount; } else { balances[sender] = -amount; } - // Add the amount to the recipient's balance if (balances[recipient] !== undefined) { balances[recipient] += amount; } else { @@ -200,13 +206,4 @@ function updateBalances(transactions) { console.log('Updated balances:', balances); } -// Start mining process at regular intervals -setInterval(() => { - miningInProgress = false; - console.log('New mining Epoch started'); - miners.forEach(miner => { - miner.send(JSON.stringify({ type: 'start_mining', difficulty })); - }); -}, targetBlockTime); - -console.log('Central server running on ws://localhost:8080'); \ No newline at end of file +console.log('Central server running on ws://localhost:8080'); diff --git a/fund-wallets.txt b/fund-wallets.txt index 1f2aab9..6455162 100644 --- a/fund-wallets.txt +++ b/fund-wallets.txt @@ -1,26 +1,26 @@ [ { - "publicKey": "04268bdfac17f95c829704682e9c1c9a0138c669594fbff50c06f570fe62548f86f28e463c86fee6ae69eb399cdf7a2ee991b46a75d134709a85433665c1048ac3", - "privateKey": "2d8b6fa8a40444c886ccbf4c1ba4fc0b8382c46ef23805dac52d629108b9df72", - "address": "35fe84406825322eadef114a2aa7cc2c56aeaa01", + "publicKey": "04182c73a4fc27552cf07184240a62f34278814d1cd03b7dc9660e4ee7af33fe48e2d420c77f9447119e1ff04f4e479c6aa6e548a1c568dc3d1b4c26964c06d29f", + "privateKey": "9f3c6d990eee1ac9b35881498235f701345c8d37e890797e1a85a66c6ee350c8", + "address": "105724cb64e414360199ec350384954cc327f403", "balance": 10000000 }, { - "publicKey": "04d7ceef69212b6b650e52228640a14a30e25c3848bad0706718e9805717c987ee42f5121e676d4d837b0542748acbf721e1549b3c1e15d9855d29cf51b0a0013f", - "privateKey": "dae05a60fc539472d4b430f532cd2ebbd61ee98aa9030b8e151de51f0c8c9d0d", - "address": "80403d5aade3bf55341cabbb7f5605ed406ad95b", + "publicKey": "0462602f2e7d2e932efb179e50a9f661ab656658464c0cefb92a83ccbf00639e9f5b949aceba988f6d713d8d7cd444b7276ae9575bff960bab57385c14ad299413", + "privateKey": "3615d096f024dc184bcc14cbffe970b63818d73793f660c0e77ba731f33403c3", + "address": "ab0013cfcf2004247f8daf73743875c5973d09e4", "balance": 10000000 }, { - "publicKey": "04d03526d3c02fa12b997f1adbcdf4c7e7c6872b7f75abc45c60cdd8901e2d24d1ff926d665552ba90ff2daa32d7f77750116d7614b923be652cd6206cc21298f5", - "privateKey": "06501429ea1a1d85dd8c7815f4bae89983ba52c95aae1d71eebaa50c1170397d", - "address": "4502b20338b776eec86dc49e7a74dcad366eaf61", + "publicKey": "044eae5b9e10d3a4f6abf14b3b53bc129117b96085ceeb228606dbf0f1c7b0c7d49eeaa5269d7fe2080e31fde3027c4d866daff8445daf69b45f38004a0edd7b18", + "privateKey": "f92e7874bef991584ef56804f25ba582da978f36955aecea9e9dc7a96af061c7", + "address": "e547679025d4b9accb74a3bf3b16d2dc535e3d4b", "balance": 10000000 }, { - "publicKey": "04c67fa6b35e62d238f78002a24863d9812c7018a82cab78574afe3a2f29aa8b1c3114b6bf02ec1d2e626938d47289b43842bea199d075ee734b107bca09ce5d30", - "privateKey": "7de779f16f4aee969e4cb16cd353012cf5b85e6937a06f542e919606e382dc0d", - "address": "9e7f08cda246e8f89324ded2b782575a74c37151", + "publicKey": "044b762b9597bf751082a163036c68553934ef4c5c4132be6293bf0034cdbac6cb8288a3e930a70696426b42b28c1ac47e9bd9d5a4a54b24b41b3396cdd4f6d500", + "privateKey": "e7e7834ce58b8aa0e0aa48f6b53f87e2f8ca89a717e8e0a5d9ca06356dab3c88", + "address": "977e79c27e956b3230db3bbc05213b5292d1fa3b", "balance": 10000000 } ] \ No newline at end of file diff --git a/minerServer.js b/minerServer.js index c9a6bf4..660aa3e 100644 --- a/minerServer.js +++ b/minerServer.js @@ -1,7 +1,7 @@ -// minerserver.js +// minerServer.js const WebSocket = require('ws'); -const crypto = require('crypto'); const express = require('express'); +const { generateBlockHash, publicKeyToAddress, createTransaction, validateTransaction, validateBlock } = require('./blockchainUtils'); const EC = require('elliptic').ec; const net = require('net'); @@ -11,6 +11,12 @@ let PORT = 3000; const ec = new EC('secp256k1'); +let miningInterval = null; // Store the mining interval +let resyncTimeout = null; // Timeout for resync after block rejection + +const resyncThreshold = 4; // Number of seconds to wait before resyncing +const miningIntervalTime = 30000; // Time in milliseconds between mining blocks + // If 3000 already in use, try 3001 or 3002 function checkPortInUse(PORT, callback) { const server = net.createServer(); @@ -49,7 +55,6 @@ function launchServer() { if (amount <= 0) { return res.status(400).send('Amount must be greater than 0'); } - //making sure recipient is a valid address if (recipient.length !== 40) { return res.status(400).send('Invalid recipient address'); } @@ -65,12 +70,12 @@ function launchServer() { res.send(balances); }); - //expose blockheight via an Express API + // Expose getBlockHeight via an Express API app.get('/getBlockHeight', (req, res) => { res.send({ blockHeight: blockchain.length }); }); - // expose getbalance of a specific address via an Express API + // Expose getBalance of a specific address via an Express API app.get('/getBalance/:address', (req, res) => { const { address } = req.params; res.send({ balance: balances[address] || 0 }); @@ -86,7 +91,6 @@ function launchServer() { let transactionPool = []; // Local pool for unconfirmed transactions let difficulty; // Difficulty will be updated by the central server let balances = {}; // Ledger to track account balances - let canMine = false; // Flag to control mining ws.on('open', () => { console.log('Connected to central server'); @@ -97,165 +101,134 @@ function launchServer() { ws.on('message', (message) => { const data = JSON.parse(message); - if (data.type === 'block') { + if (data.type === 'block_accepted') { + console.log('Block accepted:', data.block); + clearTimeout(resyncTimeout); // Clear resync timeout + blockchain.push(data.block); + updateLocalBalances(data.block.transactions); + // Start mining the next block + miningInterval = setTimeout(mineBlock, miningIntervalTime); // Adjust the interval as needed + } else if (data.type === 'block_rejected') { + console.log('Block rejected:', data.block); + console.log('Waiting for the correct block from the central server...'); + + resyncTimeout = setTimeout(() => { + console.log('No new block received. Resyncing with the central server...'); + ws.send(JSON.stringify({ type: 'request_blockchain' })); + }, resyncThreshold * 1000); + } else if (data.type === 'block') { + console.log('New block received, stopping current mining and updating blockchain.'); + clearInterval(miningInterval); // Stop current mining + clearTimeout(resyncTimeout); // Clear resync timeout handleReceivedBlock(data.block); difficulty = data.difficulty; // Update difficulty balances = data.balances; // Update balances } else if (data.type === 'blockchain') { - if (data.blockchain.length > blockchain.length) { - blockchain = data.blockchain; - difficulty = data.difficulty; // Update difficulty - balances = data.balances; // Update balances - console.log('Blockchain, difficulty, and balances updated'); - } else { - console.log('Received blockchain is not longer, ignoring'); - } + console.log('Blockchain received from central server. Validating...'); + validateReceivedBlockchain(data.blockchain, data.difficulty, data.balances); } else if (data.type === 'transaction') { console.log('Received transaction from central server, adding to pool'); transactionPool.push(data.transaction); - } else if (data.type === 'start_mining') { - difficulty = data.difficulty; // Update difficulty - canMine = true; - console.log('Received start mining signal'); - mineBlock(); - } else if (data.type === 'block_finalized') { - canMine = false; - balances = data.balances; // Sync balances after block is finalized - console.log('Mining stopped as block was finalized and balances synced'); - } else if (data.type === 'balances') { - balances = data.balances; - console.log('Balances synced:', balances); + } else if (data.type === 'transactionPool') { + console.log('Received updated transaction pool from central server'); + transactionPool = data.transactionPool; } }); - function handleReceivedBlock(newBlock) { - const lastBlock = blockchain[blockchain.length - 1]; - - if (lastBlock && lastBlock.hash === newBlock.previousHash) { - if (newBlock.index === blockchain.length) { - console.log('Block already added locally, skipping duplicate processing'); - return; - } - - blockchain.push(newBlock); - updateLocalBalances(newBlock.transactions); - console.log('New block added to the chain:', newBlock); - } else if (lastBlock && newBlock.index === blockchain.length) { - console.log('Fork detected. Comparing chains...'); - - if (validateBlock(newBlock)) { - blockchain.push(newBlock); - updateLocalBalances(newBlock.transactions); - console.log('Fork resolved: Added new block as it extends a longer chain.'); - } else { - console.log('Fork rejected: New block does not lead to a longer chain.'); - } - } else { - console.log('Invalid block rejected'); - } - } - - - function publicKeyToAddress(publicKey) { - const sha256 = crypto.createHash('sha256').update(publicKey, 'hex').digest(); - const ripemd160 = crypto.createHash('ripemd160').update(sha256).digest('hex'); - return ripemd160; - } - - function validateTransaction(transaction) { - const { publicKey, recipient, amount, timestamp, signature } = transaction; - - const key = ec.keyFromPublic(publicKey, 'hex'); - const isValid = key.verify(crypto.createHash('sha256').update(publicKey + recipient + amount + timestamp).digest(), signature); - - // for managing balances this has nothing to do with signature verification - const senderAddress = publicKeyToAddress(publicKey); - - // Check if sender has enough balance - if (balances[senderAddress] === undefined || balances[senderAddress] < amount) { - console.log('Transaction rejected due to insufficient balance'); - return false; - } - if (isValid) { - console.log('Transaction is valid'); - return true; + function validateReceivedBlockchain(receivedBlockchain, receivedDifficulty, receivedBalances) { + const localLastBlock = blockchain[blockchain.length - 1]; + const receivedLastBlock = receivedBlockchain[receivedBlockchain.length - 1]; + + // Accept the received blockchain if it's longer or if the heights are equal and hashes match + if ( + receivedBlockchain.length > blockchain.length || + (receivedBlockchain.length === blockchain.length && receivedLastBlock.hash === localLastBlock.hash) + ) { + console.log('Received blockchain is valid. Updating local blockchain.'); + blockchain = receivedBlockchain; + difficulty = receivedDifficulty; + balances = receivedBalances; + clearTimeout(resyncTimeout); // Clear resync timeout + miningInterval = setTimeout(mineBlock, miningIntervalTime); // Start mining again } else { - console.log('Transaction is invalid'); - return false; + console.log('Received blockchain is shorter and hashes do not match. Ignoring...'); } } function mineBlock() { - if (!canMine || transactionPool.length === 0) { - console.log('Cannot mine: Either mining is not allowed or no transactions to mine'); - return; - } - const previousBlock = blockchain[blockchain.length - 1]; const previousHash = previousBlock ? previousBlock.hash : ''; let nonce = 0; let hash; + const index = blockchain.length; + const timestamp = Date.now(); - const validTransactions = transactionPool.filter(validateTransaction); - - if (validTransactions.length === 0) { - console.log('No valid transactions to mine'); - return; - } + const validTransactions = transactionPool.filter(tx => validateTransaction(tx, balances)) || []; do { nonce++; - hash = crypto.createHash('sha256') - .update(previousHash + JSON.stringify(validTransactions) + nonce) - .digest('hex'); - } while (hash.substring(0, difficulty) !== '0'.repeat(difficulty) && canMine); - - if (!canMine) { - console.log('Mining was stopped'); - return; - } + hash = generateBlockHash({ + index, + timestamp, + transactions: validTransactions, + nonce, + previousHash, + }); + } while (hash.substring(0, difficulty) !== '0'.repeat(difficulty)); const block = { - index: blockchain.length, - timestamp: Date.now(), + index, + timestamp, transactions: validTransactions, nonce, hash, previousHash, }; - console.log(`Block verified: ${JSON.stringify(block)}`); - - // Clear the transaction pool immediately after mining - transactionPool = transactionPool.filter(tx => !validTransactions.includes(tx)); - - // Directly update balances before sending the block - updateLocalBalances(validTransactions); + console.log(`Block mined: ${JSON.stringify(block)}`); + // Send the newly mined block to the central server ws.send(JSON.stringify({ type: 'block', block })); - console.log('Block sent to central server'); - canMine = false; + console.log('Block sent to central server, waiting for confirmation'); } + function handleReceivedBlock(newBlock) { + const lastBlock = blockchain[blockchain.length - 1]; + + console.log('Received block:', newBlock); + console.log('Current last block:', lastBlock); + + if (lastBlock && lastBlock.hash === newBlock.previousHash && newBlock.index === lastBlock.index + 1) { + blockchain.push(newBlock); + updateLocalBalances(newBlock.transactions); + console.log('New block added to the chain:', newBlock); + + // Remove transactions from transaction pool that are in the new block + transactionPool = transactionPool.filter(tx => !newBlock.transactions.includes(tx)); + + console.log('Updated transaction pool after block acceptance:', transactionPool); + + // Start mining the next block + miningInterval = setTimeout(mineBlock, miningIntervalTime); // Adjust the interval as needed + } else { + console.log('Invalid block rejected'); + } + } - // Function to update local balances based on transactions in the received block function updateLocalBalances(transactions) { transactions.forEach(tx => { const { publicKey, recipient, amount } = tx; const sender = publicKeyToAddress(publicKey); - console.log("Sender:", sender); if (sender === undefined) { console.log('Invalid sender address'); return; } - // Deduct the amount from the sender's balance if (balances[sender] !== undefined) { balances[sender] -= amount; } - // Add the amount to the recipient's balance if (balances[recipient] !== undefined) { balances[recipient] += amount; } else { @@ -266,9 +239,8 @@ function launchServer() { console.log('Updated local balances:', balances); } - // Function to handle receiving a new transaction function handleTransaction(transaction) { - if (validateTransaction(transaction)) { + if (validateTransaction(transaction, balances)) { transactionPool.push(transaction); console.log('Transaction added to local pool and broadcasting to central server'); @@ -279,9 +251,7 @@ function launchServer() { } } - // Function to create and send a new transaction function sendTransaction(recipient, amount, privateKey) { - // Derive the public key from the private key const key = ec.keyFromPrivate(privateKey, 'hex'); const publicKey = key.getPublic().encode('hex'); const senderAddress = publicKeyToAddress(publicKey); @@ -291,21 +261,9 @@ function launchServer() { return; } - - const transaction = { - publicKey, // Use the public key as the sender - recipient, - amount, - timestamp: Date.now() - }; - - const transactionData = publicKey + recipient + amount + transaction.timestamp; - const signature = key.sign(crypto.createHash('sha256').update(transactionData).digest()).toDER('hex'); - - transaction.signature = signature; - - // Handle and broadcast the transaction + const transaction = createTransaction(publicKey, recipient, amount, privateKey); handleTransaction(transaction); } -} + setTimeout(mineBlock, miningIntervalTime); // Start mining a block after a delay +} From bd17f15ffe6b1ced128e69e90bac7bc0f7ddd028 Mon Sep 17 00:00:00 2001 From: axatbhardwaj Date: Sat, 17 Aug 2024 16:38:34 +0530 Subject: [PATCH 4/7] final working prototype --- fund-wallets.txt | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 fund-wallets.txt diff --git a/fund-wallets.txt b/fund-wallets.txt deleted file mode 100644 index 6455162..0000000 --- a/fund-wallets.txt +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "publicKey": "04182c73a4fc27552cf07184240a62f34278814d1cd03b7dc9660e4ee7af33fe48e2d420c77f9447119e1ff04f4e479c6aa6e548a1c568dc3d1b4c26964c06d29f", - "privateKey": "9f3c6d990eee1ac9b35881498235f701345c8d37e890797e1a85a66c6ee350c8", - "address": "105724cb64e414360199ec350384954cc327f403", - "balance": 10000000 - }, - { - "publicKey": "0462602f2e7d2e932efb179e50a9f661ab656658464c0cefb92a83ccbf00639e9f5b949aceba988f6d713d8d7cd444b7276ae9575bff960bab57385c14ad299413", - "privateKey": "3615d096f024dc184bcc14cbffe970b63818d73793f660c0e77ba731f33403c3", - "address": "ab0013cfcf2004247f8daf73743875c5973d09e4", - "balance": 10000000 - }, - { - "publicKey": "044eae5b9e10d3a4f6abf14b3b53bc129117b96085ceeb228606dbf0f1c7b0c7d49eeaa5269d7fe2080e31fde3027c4d866daff8445daf69b45f38004a0edd7b18", - "privateKey": "f92e7874bef991584ef56804f25ba582da978f36955aecea9e9dc7a96af061c7", - "address": "e547679025d4b9accb74a3bf3b16d2dc535e3d4b", - "balance": 10000000 - }, - { - "publicKey": "044b762b9597bf751082a163036c68553934ef4c5c4132be6293bf0034cdbac6cb8288a3e930a70696426b42b28c1ac47e9bd9d5a4a54b24b41b3396cdd4f6d500", - "privateKey": "e7e7834ce58b8aa0e0aa48f6b53f87e2f8ca89a717e8e0a5d9ca06356dab3c88", - "address": "977e79c27e956b3230db3bbc05213b5292d1fa3b", - "balance": 10000000 - } -] \ No newline at end of file From 6229a14ea4b461114480924b8cee4f7eac00bc69 Mon Sep 17 00:00:00 2001 From: axatbhardwaj Date: Sat, 17 Aug 2024 16:49:07 +0530 Subject: [PATCH 5/7] updated readme --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7ce4d63..c2dcfb2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,69 @@ -# request-for-code +# Bitcoin-like Server Project -This repository contains a bunch of interesting requests for people to code. These are things I've always wanted to build but don't get the time to get to them. +## Overview + +This project implements a simplified Bitcoin-like server, designed to simulate key aspects of the Bitcoin network. The project consists of a central WebSocket server and multiple miner servers. Each miner maintains its own transaction pool, mines blocks at regular intervals, and interacts with the central server to receive new transactions. + +## Architecture + +The project consists of the following components: + +1. **Central Server** (`centralServer.js`) + - Acts as the central hub for broadcasting transactions to all connected miners. + - Manages WebSocket connections and ensures that all miners receive the latest transactions. + - Facilitates decentralized block mining by keeping miners in sync with the transaction pool. + +2. **Miner Server** (`minerServer.js`) + - Each miner operates independently, maintaining its own transaction pool. + - Mines blocks at a fixed interval (e.g., every 30 seconds). + - Interacts with the central server to receive new transactions and broadcast mined blocks. + +3. **Blockchain Utilities** (`blockchainUtils.js`) + - Contains utility functions for blockchain operations, including block creation, transaction handling, and validation. + - Ensures consistent and reliable blockchain functionality across all servers. + +## Usage + +Use the `fund-wallets.txt` file to view the initial accounts created to send transactions. + +### API Endpoints + +The following APIs are exposed via an Express server: + +- **Send Transaction**: Allows sending transactions to a recipient. + - **Endpoint**: `POST /sendTransaction` + - **Request Body**: + - `recipient` (string): The recipient's address (40 characters). + - `amount` (number): The amount to send (must be greater than 0). + - `privateKey` (string): The private key for signing the transaction (64 characters). + - **Response**: `Transaction processed` if successful, with error messages for invalid inputs. + +- **Get Balances**: Retrieves the balance of all accounts. + - **Endpoint**: `GET /getBalances` + - **Response**: A JSON object containing all account balances. + +- **Get Block Height**: Retrieves the current block height of the blockchain. + - **Endpoint**: `GET /getBlockHeight` + - **Response**: A JSON object with the current block height. + +- **Get Balance**: Retrieves the balance of a specific address. + - **Endpoint**: `GET /getBalance/:address` + - **Response**: A JSON object with the balance of the specified address. + +### Running the Servers + +To run the project, follow these steps: + +1. **Start the Central Server**: + ```bash + node centralServer.js + +2. **Start the Miner Servers**: + ```bash + node minerServer.js + ``` + +3. **Access the API Endpoints**: + - Send transactions to the central server using the provided APIs. + - View the balances of all accounts using the `/getBalances` endpoint. + - Monitor the block height using the `/getBlockHeight` endpoint. \ No newline at end of file From 10b4e3f4f48e5a19bb53d3f457ede25c5d3e9368 Mon Sep 17 00:00:00 2001 From: Dushyant Goswami Date: Sun, 18 Aug 2024 23:24:20 +0530 Subject: [PATCH 6/7] feat: add rpc support and fix recipient amount bug --- blockchainUtils.js | 15 +- centralServer.js | 469 +++++++++++++++--------- minerServer.js | 529 +++++++++++++++------------ package-lock.json | 871 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 5 files changed, 1487 insertions(+), 398 deletions(-) create mode 100644 package-lock.json diff --git a/blockchainUtils.js b/blockchainUtils.js index 4f15afc..9e7116e 100644 --- a/blockchainUtils.js +++ b/blockchainUtils.js @@ -15,16 +15,21 @@ function publicKeyToAddress(publicKey) { return ripemd160; } -function createTransaction(publicKey, recipient, amount, privateKey) { +function createTransaction(publicKey, senderAddress, recipient, amount, privateKey) { const key = ec.keyFromPrivate(privateKey, 'hex'); + + //convert string amount to number + amount = parseFloat(amount); + const transaction = { publicKey, + senderAddress, recipient, amount, timestamp: Date.now(), }; - const transactionData = publicKey + recipient + amount + transaction.timestamp; + const transactionData = publicKey + senderAddress + recipient + amount + transaction.timestamp; const transactionHash = crypto.createHash('sha256').update(transactionData).digest('hex'); const signature = key.sign(crypto.createHash('sha256').update(transactionData).digest()).toDER('hex'); @@ -34,11 +39,11 @@ function createTransaction(publicKey, recipient, amount, privateKey) { } function validateTransaction(transaction, balances) { - const { publicKey, recipient, amount, timestamp, signature } = transaction; + const { publicKey, senderAddress , recipient, amount, timestamp, signature } = transaction; const key = ec.keyFromPublic(publicKey, 'hex'); - const isValid = key.verify(crypto.createHash('sha256').update(publicKey + recipient + amount + timestamp).digest(), signature); + const isValid = key.verify(crypto.createHash('sha256').update(publicKey + senderAddress + recipient + amount + timestamp).digest(), signature); - const senderAddress = publicKeyToAddress(publicKey); + if (balances[senderAddress] === undefined || balances[senderAddress] < amount) { return false; } diff --git a/centralServer.js b/centralServer.js index 4cf2b62..8ba5b4b 100644 --- a/centralServer.js +++ b/centralServer.js @@ -1,209 +1,342 @@ -// centralServer.js -const WebSocket = require('ws'); -const fs = require('fs'); -const { generateBlockHash, publicKeyToAddress, validateBlock } = require('./blockchainUtils'); -const EC = require('elliptic').ec; - -const ec = new EC('secp256k1'); +// centralServer.js +const express = require("express"); +const WebSocket = require("ws"); +const fs = require("fs"); +const { + generateBlockHash, + publicKeyToAddress, + validateBlock, +} = require("./blockchainUtils"); +const EC = require("elliptic").ec; +const cors = require("cors"); + +const app = express(); +app.use(express.json()); +app.use(cors()); + +const ec = new EC("secp256k1"); const wss = new WebSocket.Server({ port: 8080 }); +const Central_RPC_PORT = 8081; -let blockchain = []; // Store blockchain in-memory +let blockchain = []; // Store blockchain in-memory let miners = []; -let difficulty = 4; // Initial difficulty: requires hash to start with '0000' -const adjustmentInterval = 5; // Adjust difficulty every 5 blocks -let balances = {}; // Ledger to track account balances -const desiredBlockTime = 30000; // 30 seconds +let difficulty = 4; // Initial difficulty: requires hash to start with '0000' +const adjustmentInterval = 5; // Adjust difficulty every 5 blocks +let balances = {}; // Ledger to track account balances +const desiredBlockTime = 30000; // 30 seconds let transactionPool = []; // Central server's transaction pool function createWallet() { - const keyPair = ec.genKeyPair(); - const privateKey = keyPair.getPrivate('hex'); - const publicKey = keyPair.getPublic('hex'); - const address = publicKeyToAddress(publicKey); + const keyPair = ec.genKeyPair(); + const privateKey = keyPair.getPrivate("hex"); + const publicKey = keyPair.getPublic("hex"); + const address = publicKeyToAddress(publicKey); - return { publicKey, privateKey, address }; + return { publicKey, privateKey, address }; } async function initializeBlockchain() { - const wallets = []; - const initialBalance = 10000000; - - for (let i = 0; i < 4; i++) { - const wallet = createWallet(); - balances[wallet.address] = initialBalance; + const wallets = []; + const initialBalance = 10000000; + + for (let i = 0; i < 4; i++) { + const wallet = createWallet(); + balances[wallet.address] = initialBalance; + + wallets.push({ + publicKey: wallet.publicKey, + privateKey: wallet.privateKey, + address: wallet.address, + balance: initialBalance, + }); + } + + console.log("Balances:", balances); + + fs.writeFileSync("fund-wallets.txt", JSON.stringify(wallets, null, 2)); + + const genesisTimestamp = Date.now(); + const genesisBlock = { + index: 0, + previousHash: "0".repeat(64), + timestamp: genesisTimestamp, + transactions: [], + nonce: 0, + hash: generateBlockHash({ + index: 0, + previousHash: "0".repeat(64), + timestamp: genesisTimestamp, + transactions: [], + nonce: 0, + }), + }; + + blockchain.push(genesisBlock); +} - wallets.push({ publicKey: wallet.publicKey, privateKey: wallet.privateKey, address: wallet.address, balance: initialBalance }); +wss.on("connection", (ws) => { + miners.push(ws); + console.log("New miner connected"); + + if (blockchain.length === 0) { + initializeBlockchain(); + console.log("Blockchain initialized"); + } + + ws.on("message", (message) => { + const data = JSON.parse(message); + + if (data.type === "request_blockchain") { + ws.send( + JSON.stringify({ type: "blockchain", blockchain, difficulty, balances }) + ); + } else if (data.type === "block") { + if (validateAndAddBlock(data.block)) { + console.log("Block mined and accepted, broadcasting to other miners"); + + // Notify the miner that its block was accepted + ws.send(JSON.stringify({ type: "block_accepted", block: data.block })); + + // Broadcast the accepted block to all other miners + miners.forEach((miner) => { + miner.send( + JSON.stringify({ + type: "block", + block: data.block, + difficulty, + balances, + }) + ); + }); + + if (blockchain.length % adjustmentInterval === 0) { + adjustDifficulty(); + } + } else { + console.log("Block rejected due to validation failure"); + + // Notify the miner that its block was rejected + ws.send(JSON.stringify({ type: "block_rejected", block: data.block })); + } + } else if (data.type === "transaction") { + miners.forEach((miner) => { + if (miner !== ws) { + miner.send( + JSON.stringify({ + type: "transaction", + transaction: data.transaction, + }) + ); + } + }); + console.log("Transaction broadcasted to all miners"); } + }); - console.log('Balances:', balances); + ws.on("close", () => { + miners = miners.filter((miner) => miner !== ws); + console.log("Miner disconnected"); + }); +}); - fs.writeFileSync('fund-wallets.txt', JSON.stringify(wallets, null, 2)); +function validateAndAddBlock(newBlock) { + const lastBlock = blockchain[blockchain.length - 1]; + console.log("Validating new block:", newBlock); + + if (lastBlock) { + console.log("Last block:", lastBlock); + } + + if ( + lastBlock && + lastBlock.hash === newBlock.previousHash && + newBlock.index === lastBlock.index + 1 && + validateBlock(newBlock, difficulty) + ) { + blockchain.push(newBlock); + updateBalances(newBlock.transactions); + console.log("Block added to blockchain:", newBlock); + + // Remove transactions included in the new block from the transaction pool + removeMinedTransactions(newBlock.transactions); + + // Broadcast the updated transaction pool to all miners + broadcastTransactionPool(); + + return true; + } else if (!lastBlock && newBlock.index === 0) { + blockchain.push(newBlock); + updateBalances(newBlock.transactions); + console.log("Genesis block added to blockchain:", newBlock); + return true; + } else { + console.error("Block validation failed"); + if (lastBlock.hash !== newBlock.previousHash) { + console.error("Previous hash mismatch"); + } + if (newBlock.index !== lastBlock.index + 1) { + console.error("Index mismatch"); + } + if (!validateBlock(newBlock, difficulty)) { + console.error("Block does not satisfy the difficulty requirement"); + } + } + return false; +} - const genesisTimestamp = Date.now(); - const genesisBlock = { - index: 0, - previousHash: '0'.repeat(64), - timestamp: genesisTimestamp, - transactions: [], - nonce: 0, - hash: generateBlockHash({ index: 0, previousHash: '0'.repeat(64), timestamp: genesisTimestamp, transactions: [], nonce: 0 }) - }; +function removeMinedTransactions(minedTransactions) { + transactionPool = transactionPool.filter((tx) => { + return !minedTransactions.some((minedTx) => minedTx.hash === tx.hash); + }); + console.log("Updated transaction pool after mining:", transactionPool); +} - blockchain.push(genesisBlock); +function broadcastTransactionPool() { + miners.forEach((miner) => { + miner.send(JSON.stringify({ type: "transactionPool", transactionPool })); + }); + console.log("Broadcasted updated transaction pool to all miners"); } -wss.on('connection', (ws) => { - miners.push(ws); - console.log('New miner connected'); +function adjustDifficulty() { + const blocksToConsider = blockchain.slice(-adjustmentInterval); - if (blockchain.length === 0) { - initializeBlockchain(); - console.log('Blockchain initialized'); + const totalBlockTime = blocksToConsider.reduce((total, block, index) => { + if (index > 0) { + return total + (block.timestamp - blocksToConsider[index - 1].timestamp); } + return total; + }, 0); + + const averageBlockTime = totalBlockTime / (blocksToConsider.length - 1); + + if (averageBlockTime < desiredBlockTime) { + // 30 seconds in milliseconds + difficulty++; + console.log("Difficulty increased to:", difficulty); + } else if (averageBlockTime > desiredBlockTime && difficulty > 1) { + difficulty--; + console.log("Difficulty decreased to:", difficulty); + } +} - ws.on('message', (message) => { - const data = JSON.parse(message); - - if (data.type === 'request_blockchain') { - ws.send(JSON.stringify({ type: 'blockchain', blockchain, difficulty, balances })); - } else if (data.type === 'block') { - if (validateAndAddBlock(data.block)) { - console.log('Block mined and accepted, broadcasting to other miners'); - - // Notify the miner that its block was accepted - ws.send(JSON.stringify({ type: 'block_accepted', block: data.block })); - - // Broadcast the accepted block to all other miners - miners.forEach(miner => { - miner.send(JSON.stringify({ type: 'block', block: data.block, difficulty, balances })); - }); - - if (blockchain.length % adjustmentInterval === 0) { - adjustDifficulty(); - } - } else { - console.log('Block rejected due to validation failure'); - - // Notify the miner that its block was rejected - ws.send(JSON.stringify({ type: 'block_rejected', block: data.block })); - } - } else if (data.type === 'transaction') { - miners.forEach(miner => { - if (miner !== ws) { - miner.send(JSON.stringify({ type: 'transaction', transaction: data.transaction })); - } - }); - console.log('Transaction broadcasted to all miners'); - } - }); - - ws.on('close', () => { - miners = miners.filter(miner => miner !== ws); - console.log('Miner disconnected'); - }); -}); +function updateBalances(transactions) { + transactions.forEach((tx) => { + const { publicKey, recipient, amount } = tx; -function validateAndAddBlock(newBlock) { - const lastBlock = blockchain[blockchain.length - 1]; - console.log('Validating new block:', newBlock); + const sender = publicKeyToAddress(publicKey); + if (sender === undefined) { + console.log("Invalid sender address"); + return; + } + if (balances[sender] !== undefined) { + balances[sender] -= amount; + } else { + balances[sender] = -amount; + } - if (lastBlock) { - console.log('Last block:', lastBlock); + if (balances[recipient] !== undefined) { + balances[recipient] += amount; + } else { + balances[recipient] = amount; } + }); - if (lastBlock && lastBlock.hash === newBlock.previousHash && - newBlock.index === lastBlock.index + 1 && - validateBlock(newBlock, difficulty)) { + console.log("Updated balances:", balances); +} - blockchain.push(newBlock); - updateBalances(newBlock.transactions); - console.log('Block added to blockchain:', newBlock); +//RPC ENDPOINTS - // Remove transactions included in the new block from the transaction pool - removeMinedTransactions(newBlock.transactions); +// Return the entire blockchain history +app.get("/getBlockchainStats", (req, res) => { + res.json({ blockchain, difficulty }); +}); - // Broadcast the updated transaction pool to all miners - broadcastTransactionPool(); +// Get Block by Index +app.get("/getBlock/:index", (req, res) => { + const { index } = req.params; + const block = blockchain.find((b) => b.index == index); + if (!block) { + return res.status(404).json({ error: "Block not found" }); + } + res.json(block); +}); - return true; - } else if (!lastBlock && newBlock.index === 0) { - blockchain.push(newBlock); - updateBalances(newBlock.transactions); - console.log('Genesis block added to blockchain:', newBlock); - return true; - } else { - console.error('Block validation failed'); - if (lastBlock.hash !== newBlock.previousHash) { - console.error('Previous hash mismatch'); - } - if (newBlock.index !== lastBlock.index + 1) { - console.error('Index mismatch'); - } - if (!validateBlock(newBlock, difficulty)) { - console.error('Block does not satisfy the difficulty requirement'); - } - } - return false; -} +// Get Latest Block +app.get("/getLatestBlock", (req, res) => { + const latestBlock = blockchain[blockchain.length - 1]; + res.json(latestBlock); +}); -function removeMinedTransactions(minedTransactions) { - transactionPool = transactionPool.filter(tx => { - return !minedTransactions.some(minedTx => minedTx.hash === tx.hash); - }); - console.log('Updated transaction pool after mining:', transactionPool); -} +// Get Transaction by Hash +app.get("/getTransaction/:hash", (req, res) => { + const { hash } = req.params; + let transaction = null; -function broadcastTransactionPool() { - miners.forEach(miner => { - miner.send(JSON.stringify({ type: 'transactionPool', transactionPool })); + blockchain.forEach((block) => { + block.transactions.forEach((tx) => { + if (tx.hash === hash) { + transaction = tx; + } }); - console.log('Broadcasted updated transaction pool to all miners'); -} + }); -function adjustDifficulty() { - const blocksToConsider = blockchain.slice(-adjustmentInterval); + if (!transaction) { + return res.status(404).json({ error: "Transaction not found" }); + } - const totalBlockTime = blocksToConsider.reduce((total, block, index) => { - if (index > 0) { - return total + (block.timestamp - blocksToConsider[index - 1].timestamp); - } - return total; - }, 0); + res.json(transaction); +}); - const averageBlockTime = totalBlockTime / (blocksToConsider.length - 1); +// Get Transaction History for a Specific Address +app.get("/getTransactionHistory/:address", (req, res) => { + const { address } = req.params; + let transactionHistory = []; + + blockchain.forEach((block) => { + block.transactions.forEach((tx) => { + if (tx.senderAddress === address || tx.recipient === address) { + transactionHistory.push({ + ...tx, + status: "confirmed", + blockIndex: block.index, + timestamp: block.timestamp, + }); + } + }); + }); + res.json({"Transactions": transactionHistory}); +}); - if (averageBlockTime < desiredBlockTime) { // 30 seconds in milliseconds - difficulty++; - console.log('Difficulty increased to:', difficulty); - } else if (averageBlockTime > desiredBlockTime && difficulty > 1) { - difficulty--; - console.log('Difficulty decreased to:', difficulty); - } -} +// Get Transaction Count for a Specific Address +app.get("/getTransactionCount/:address", (req, res) => { + const { address } = req.params; + let transactionCount = 0; -function updateBalances(transactions) { - transactions.forEach(tx => { - const { publicKey, recipient, amount } = tx; + blockchain.forEach((block) => { + block.transactions.forEach((tx) => { + if (tx.senderAddress === address || tx.recipient === address) { + transactionCount++; + } + }); + }); - const sender = publicKeyToAddress(publicKey); - if (sender === undefined) { - console.log('Invalid sender address'); - return; - } - if (balances[sender] !== undefined) { - balances[sender] -= amount; - } else { - balances[sender] = -amount; - } + const pendingTransactions = transactionPool.filter( + (tx) => tx.senderAddress === address || tx.recipient === address + ); + transactionCount += pendingTransactions.length; - if (balances[recipient] !== undefined) { - balances[recipient] += amount; - } else { - balances[recipient] = amount; - } - }); + res.json({ address, transactionCount }); +}); - console.log('Updated balances:', balances); -} +//Return Difficulty +app.get("/getDifficulty", (req, res) => { + res.json({ "Current Difficulty": difficulty }); +}); -console.log('Central server running on ws://localhost:8080'); +app.listen(Central_RPC_PORT, () => { + console.log( + `Central Server RPC server running on http://localhost:${Central_RPC_PORT}` + ); +}); +console.log("Central server running on ws://localhost:8080"); diff --git a/minerServer.js b/minerServer.js index 660aa3e..d445fa7 100644 --- a/minerServer.js +++ b/minerServer.js @@ -1,15 +1,21 @@ // minerServer.js -const WebSocket = require('ws'); -const express = require('express'); -const { generateBlockHash, publicKeyToAddress, createTransaction, validateTransaction, validateBlock } = require('./blockchainUtils'); -const EC = require('elliptic').ec; -const net = require('net'); +const WebSocket = require("ws"); +const express = require("express"); +const { + generateBlockHash, + publicKeyToAddress, + createTransaction, + validateTransaction, + validateBlock, +} = require("./blockchainUtils"); +const EC = require("elliptic").ec; +const net = require("net"); const app = express(); app.use(express.json()); let PORT = 3000; -const ec = new EC('secp256k1'); +const ec = new EC("secp256k1"); let miningInterval = null; // Store the mining interval let resyncTimeout = null; // Timeout for resync after block rejection @@ -19,251 +25,324 @@ const miningIntervalTime = 30000; // Time in milliseconds between mining blocks // If 3000 already in use, try 3001 or 3002 function checkPortInUse(PORT, callback) { - const server = net.createServer(); - server.once('error', function (err) { - if (err.code === 'EADDRINUSE') { - console.log(`Port ${PORT} is in use, trying another port`); - PORT++; - checkPortInUse(PORT, callback); - } else { - callback(err); - } - }); + const server = net.createServer(); + server.once("error", function (err) { + if (err.code === "EADDRINUSE") { + console.log(`Port ${PORT} is in use, trying another port`); + PORT++; + checkPortInUse(PORT, callback); + } else { + callback(err); + } + }); - server.once('listening', function () { - server.close(); - callback(null, PORT); - }); + server.once("listening", function () { + server.close(); + callback(null, PORT); + }); - server.listen(PORT); + server.listen(PORT); } checkPortInUse(PORT, (err, availablePort) => { - if (err) { - console.error('Error checking port:', err); - } else { - PORT = availablePort; - console.log("Launching server on port", PORT); - launchServer(); - } + if (err) { + console.error("Error checking port:", err); + } else { + PORT = availablePort; + console.log("Launching server on port", PORT); + launchServer(); + } }); function launchServer() { - // Expose sendTransaction via an Express API - app.post('/sendTransaction', (req, res) => { - const { recipient, amount, privateKey } = req.body; - if (amount <= 0) { - return res.status(400).send('Amount must be greater than 0'); - } - if (recipient.length !== 40) { - return res.status(400).send('Invalid recipient address'); - } - if (privateKey.length !== 64) { - return res.status(400).send('Invalid private key'); - } - sendTransaction(recipient, amount, privateKey); - res.send('Transaction processed'); - }); - - // Expose getBalances via an Express API - app.get('/getBalances', (req, res) => { - res.send(balances); - }); - - // Expose getBlockHeight via an Express API - app.get('/getBlockHeight', (req, res) => { - res.send({ blockHeight: blockchain.length }); - }); - - // Expose getBalance of a specific address via an Express API - app.get('/getBalance/:address', (req, res) => { - const { address } = req.params; - res.send({ balance: balances[address] || 0 }); - }); - - app.listen(PORT, () => { - console.log(`Server running on port ${PORT}`); - }); - const ws = new WebSocket('ws://localhost:8080'); + const ws = new WebSocket("ws://localhost:8080"); - let blockchain = []; // Store blockchain in-memory - let transactionPool = []; // Local pool for unconfirmed transactions - let difficulty; // Difficulty will be updated by the central server - let balances = {}; // Ledger to track account balances + let blockchain = []; // Store blockchain in-memory + let transactionPool = []; // Local pool for unconfirmed transactions + let difficulty; // Difficulty will be updated by the central server + let balances = {}; // Ledger to track account balances - ws.on('open', () => { - console.log('Connected to central server'); - // Request the current blockchain and balances from the central server - ws.send(JSON.stringify({ type: 'request_blockchain' })); + app.post("/sendTransaction", (req, res) => { + const { recipient, amount, privateKey } = req.body; + if (amount <= 0) { + return res.status(400).send("Amount must be greater than 0"); + } + if (recipient.length !== 40) { + return res.status(400).send("Invalid recipient address"); + } + if (privateKey.length !== 64) { + return res.status(400).send("Invalid private key"); + } + sendTransaction(recipient, amount, privateKey); + res.send("Transaction processed"); + }); + + // Expose getBalances via an Express API + app.get("/getBalances", (req, res) => { + res.send(balances); + }); + + console.log("Sending balances response:", balances); + // Expose getBlockHeight via an Express API + app.get("/getBlockHeight", (req, res) => { + res.send({ blockHeight: blockchain.length }); + }); + + // Expose getBalance of a specific address via an Express API + app.get("/getBalance/:address", (req, res) => { + const { address } = req.params; + res.send({ balance: balances[address] || 0 }); + }); + + // Get Mining Info + app.get("/miningInfo", (req, res) => { + res.json({ + mining: miningInterval !== null, + difficulty, + blockHeight: blockchain.length, }); - - ws.on('message', (message) => { - const data = JSON.parse(message); - - if (data.type === 'block_accepted') { - console.log('Block accepted:', data.block); - clearTimeout(resyncTimeout); // Clear resync timeout - blockchain.push(data.block); - updateLocalBalances(data.block.transactions); - // Start mining the next block - miningInterval = setTimeout(mineBlock, miningIntervalTime); // Adjust the interval as needed - } else if (data.type === 'block_rejected') { - console.log('Block rejected:', data.block); - console.log('Waiting for the correct block from the central server...'); - - resyncTimeout = setTimeout(() => { - console.log('No new block received. Resyncing with the central server...'); - ws.send(JSON.stringify({ type: 'request_blockchain' })); - }, resyncThreshold * 1000); - } else if (data.type === 'block') { - console.log('New block received, stopping current mining and updating blockchain.'); - clearInterval(miningInterval); // Stop current mining - clearTimeout(resyncTimeout); // Clear resync timeout - handleReceivedBlock(data.block); - difficulty = data.difficulty; // Update difficulty - balances = data.balances; // Update balances - } else if (data.type === 'blockchain') { - console.log('Blockchain received from central server. Validating...'); - validateReceivedBlockchain(data.blockchain, data.difficulty, data.balances); - } else if (data.type === 'transaction') { - console.log('Received transaction from central server, adding to pool'); - transactionPool.push(data.transaction); - } else if (data.type === 'transactionPool') { - console.log('Received updated transaction pool from central server'); - transactionPool = data.transactionPool; - } + }); + + // Get Pending Transactions + app.get("/pendingTransactions", (req, res) => { + res.json({ transactionPool }); + }); + + // Get Pending Transactions by Sender Address + app.get("/getPendingTransactionsByAccount/:address", (req, res) => { + const { address } = req.params; + + const pendingTransactions = transactionPool.filter((tx) => { + const senderAddress = publicKeyToAddress(tx.publicKey); + + return ( + senderAddress === address|| tx.recipient === address + ); }); - function validateReceivedBlockchain(receivedBlockchain, receivedDifficulty, receivedBalances) { - const localLastBlock = blockchain[blockchain.length - 1]; - const receivedLastBlock = receivedBlockchain[receivedBlockchain.length - 1]; - - // Accept the received blockchain if it's longer or if the heights are equal and hashes match - if ( - receivedBlockchain.length > blockchain.length || - (receivedBlockchain.length === blockchain.length && receivedLastBlock.hash === localLastBlock.hash) - ) { - console.log('Received blockchain is valid. Updating local blockchain.'); - blockchain = receivedBlockchain; - difficulty = receivedDifficulty; - balances = receivedBalances; - clearTimeout(resyncTimeout); // Clear resync timeout - miningInterval = setTimeout(mineBlock, miningIntervalTime); // Start mining again - } else { - console.log('Received blockchain is shorter and hashes do not match. Ignoring...'); - } + if (pendingTransactions.length === 0) { + return res + .status(404) + .json({ error: "No pending transactions found for this address" }); } - function mineBlock() { - const previousBlock = blockchain[blockchain.length - 1]; - const previousHash = previousBlock ? previousBlock.hash : ''; - let nonce = 0; - let hash; - const index = blockchain.length; - const timestamp = Date.now(); - - const validTransactions = transactionPool.filter(tx => validateTransaction(tx, balances)) || []; - - do { - nonce++; - hash = generateBlockHash({ - index, - timestamp, - transactions: validTransactions, - nonce, - previousHash, - }); - } while (hash.substring(0, difficulty) !== '0'.repeat(difficulty)); - - const block = { - index, - timestamp, - transactions: validTransactions, - nonce, - hash, - previousHash, - }; - - console.log(`Block mined: ${JSON.stringify(block)}`); - - // Send the newly mined block to the central server - ws.send(JSON.stringify({ type: 'block', block })); - console.log('Block sent to central server, waiting for confirmation'); + res.json({ pendingTransactions }); + }); + + app.listen(PORT, () => { + console.log(`Server running on port ${PORT}`); + }); + + + ws.on("open", () => { + console.log("Connected to central server"); + // Request the current blockchain and balances from the central server + ws.send(JSON.stringify({ type: "request_blockchain" })); + }); + + ws.on("message", (message) => { + const data = JSON.parse(message); + + if (data.type === "block_accepted") { + console.log("Block accepted:", data.block); + clearTimeout(resyncTimeout); // Clear resync timeout + blockchain.push(data.block); + updateLocalBalances(data.block.transactions); + // Start mining the next block + miningInterval = setTimeout(mineBlock, miningIntervalTime); // Adjust the interval as needed + } else if (data.type === "block_rejected") { + console.log("Block rejected:", data.block); + console.log("Waiting for the correct block from the central server..."); + + resyncTimeout = setTimeout(() => { + console.log( + "No new block received. Resyncing with the central server..." + ); + ws.send(JSON.stringify({ type: "request_blockchain" })); + }, resyncThreshold * 1000); + } else if (data.type === "block") { + console.log( + "New block received, stopping current mining and updating blockchain." + ); + clearInterval(miningInterval); // Stop current mining + clearTimeout(resyncTimeout); // Clear resync timeout + handleReceivedBlock(data.block); + difficulty = data.difficulty; // Update difficulty + balances = data.balances; // Update balances + } else if (data.type === "blockchain") { + console.log("Blockchain received from central server. Validating..."); + validateReceivedBlockchain( + data.blockchain, + data.difficulty, + data.balances + ); + } else if (data.type === "transaction") { + console.log("Received transaction from central server, adding to pool"); + transactionPool.push(data.transaction); + } else if (data.type === "transactionPool") { + console.log("Received updated transaction pool from central server"); + transactionPool = data.transactionPool; } - - function handleReceivedBlock(newBlock) { - const lastBlock = blockchain[blockchain.length - 1]; - - console.log('Received block:', newBlock); - console.log('Current last block:', lastBlock); - - if (lastBlock && lastBlock.hash === newBlock.previousHash && newBlock.index === lastBlock.index + 1) { - blockchain.push(newBlock); - updateLocalBalances(newBlock.transactions); - console.log('New block added to the chain:', newBlock); - - // Remove transactions from transaction pool that are in the new block - transactionPool = transactionPool.filter(tx => !newBlock.transactions.includes(tx)); - - console.log('Updated transaction pool after block acceptance:', transactionPool); - - // Start mining the next block - miningInterval = setTimeout(mineBlock, miningIntervalTime); // Adjust the interval as needed - } else { - console.log('Invalid block rejected'); - } + }); + + function validateReceivedBlockchain( + receivedBlockchain, + receivedDifficulty, + receivedBalances + ) { + const localLastBlock = blockchain[blockchain.length - 1]; + const receivedLastBlock = receivedBlockchain[receivedBlockchain.length - 1]; + + // Accept the received blockchain if it's longer or if the heights are equal and hashes match + if ( + receivedBlockchain.length > blockchain.length || + (receivedBlockchain.length === blockchain.length && + receivedLastBlock.hash === localLastBlock.hash) + ) { + console.log("Received blockchain is valid. Updating local blockchain."); + blockchain = receivedBlockchain; + difficulty = receivedDifficulty; + balances = receivedBalances; + clearTimeout(resyncTimeout); // Clear resync timeout + miningInterval = setTimeout(mineBlock, miningIntervalTime); // Start mining again + } else { + console.log( + "Received blockchain is shorter and hashes do not match. Ignoring..." + ); } - - function updateLocalBalances(transactions) { - transactions.forEach(tx => { - const { publicKey, recipient, amount } = tx; - - const sender = publicKeyToAddress(publicKey); - if (sender === undefined) { - console.log('Invalid sender address'); - return; - } - - if (balances[sender] !== undefined) { - balances[sender] -= amount; - } - - if (balances[recipient] !== undefined) { - balances[recipient] += amount; - } else { - balances[recipient] = amount; - } - }); - - console.log('Updated local balances:', balances); + } + + function mineBlock() { + const previousBlock = blockchain[blockchain.length - 1]; + const previousHash = previousBlock ? previousBlock.hash : ""; + let nonce = 0; + let hash; + const index = blockchain.length; + const timestamp = Date.now(); + + const validTransactions = + transactionPool.filter((tx) => validateTransaction(tx, balances)) || []; + + do { + nonce++; + hash = generateBlockHash({ + index, + timestamp, + transactions: validTransactions, + nonce, + previousHash, + }); + } while (hash.substring(0, difficulty) !== "0".repeat(difficulty)); + + const block = { + index, + timestamp, + transactions: validTransactions, + nonce, + hash, + previousHash, + }; + + console.log(`Block mined: ${JSON.stringify(block)}`); + + // Send the newly mined block to the central server + ws.send(JSON.stringify({ type: "block", block })); + console.log("Block sent to central server, waiting for confirmation"); + } + + function handleReceivedBlock(newBlock) { + const lastBlock = blockchain[blockchain.length - 1]; + + console.log("Received block:", newBlock); + console.log("Current last block:", lastBlock); + + if ( + lastBlock && + lastBlock.hash === newBlock.previousHash && + newBlock.index === lastBlock.index + 1 + ) { + blockchain.push(newBlock); + updateLocalBalances(newBlock.transactions); + console.log("New block added to the chain:", newBlock); + + // Remove transactions from transaction pool that are in the new block + transactionPool = transactionPool.filter( + (tx) => !newBlock.transactions.includes(tx) + ); + + console.log( + "Updated transaction pool after block acceptance:", + transactionPool + ); + + // Start mining the next block + miningInterval = setTimeout(mineBlock, miningIntervalTime); // Adjust the interval as needed + } else { + console.log("Invalid block rejected"); } + } - function handleTransaction(transaction) { - if (validateTransaction(transaction, balances)) { - transactionPool.push(transaction); - console.log('Transaction added to local pool and broadcasting to central server'); + function updateLocalBalances(transactions) { + transactions.forEach(tx => { + const { publicKey, recipient, amount } = tx; - // Broadcast the valid transaction to the central server - ws.send(JSON.stringify({ type: 'transaction', transaction })); - } else { - console.log('Invalid transaction rejected'); + const sender = publicKeyToAddress(publicKey); + if (sender === undefined) { + console.log('Invalid sender address'); + return; } - } - function sendTransaction(recipient, amount, privateKey) { - const key = ec.keyFromPrivate(privateKey, 'hex'); - const publicKey = key.getPublic().encode('hex'); - const senderAddress = publicKeyToAddress(publicKey); + if (balances[sender] !== undefined) { + balances[sender] -= amount; + } - if (balances[senderAddress] === undefined || balances[senderAddress] < amount) { - console.log('Insufficient balance to send the transaction'); - return; + if (balances[recipient] !== undefined) { + balances[recipient] += amount; + } else { + balances[recipient] = amount; } + }); - const transaction = createTransaction(publicKey, recipient, amount, privateKey); - handleTransaction(transaction); + console.log('Updated local balances:', balances); +} + + function handleTransaction(transaction) { + if (validateTransaction(transaction, balances)) { + transactionPool.push(transaction); + console.log( + "Transaction added to local pool and broadcasting to central server" + ); + + // Broadcast the valid transaction to the central server + ws.send(JSON.stringify({ type: "transaction", transaction })); + } else { + console.log("Invalid transaction rejected"); + } + } + + function sendTransaction(recipient, amount, privateKey) { + const key = ec.keyFromPrivate(privateKey, "hex"); + const publicKey = key.getPublic().encode("hex"); + const senderAddress = publicKeyToAddress(publicKey); + + if ( + balances[senderAddress] === undefined || + balances[senderAddress] < amount + ) { + console.log("Insufficient balance to send the transaction"); + return; } - setTimeout(mineBlock, miningIntervalTime); // Start mining a block after a delay + const transaction = createTransaction( + publicKey, + senderAddress, + recipient, + amount, + privateKey + ); + handleTransaction(transaction); + } + + setTimeout(mineBlock, miningIntervalTime); // Start mining a block after a delay } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e6e5c9c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,871 @@ +{ + "name": "request-for-code", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "cors": "^2.8.5", + "crypto": "^1.0.1", + "elliptic": "^6.5.7", + "express": "^4.19.2", + "ws": "^8.18.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", + "license": "ISC" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/elliptic": { + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/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==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json index 3434a8f..69df821 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "dependencies": { + "cors": "^2.8.5", "crypto": "^1.0.1", "elliptic": "^6.5.7", "express": "^4.19.2", From 45818169272ed213eedc21b5e9cac238f04f43e1 Mon Sep 17 00:00:00 2001 From: Dushyant Goswami Date: Mon, 19 Aug 2024 05:09:40 +0530 Subject: [PATCH 7/7] fix --- blockchainUtils.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/blockchainUtils.js b/blockchainUtils.js index 9e7116e..139f369 100644 --- a/blockchainUtils.js +++ b/blockchainUtils.js @@ -18,9 +18,6 @@ function publicKeyToAddress(publicKey) { function createTransaction(publicKey, senderAddress, recipient, amount, privateKey) { const key = ec.keyFromPrivate(privateKey, 'hex'); - //convert string amount to number - amount = parseFloat(amount); - const transaction = { publicKey, senderAddress,