Repository: seanseany/blockchain Branch: master Commit: e03e2bbea668 Files: 11 Total size: 14.1 KB Directory structure: gitextract_mjm9mmw7/ ├── .gitignore ├── Block.js ├── Blockchain.js ├── LICENSE.ISC.txt ├── Messages.js ├── P2p.js ├── README.md ├── cli.js ├── index.js ├── message-type.js └── package.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules .DS_Store ================================================ FILE: Block.js ================================================ class Block { constructor (index, previousHash, timestamp, data, hash, nonce) { this.index = index; this.previousHash = previousHash; this.timestamp = timestamp; this.data = data; this.hash = hash; this.nonce = nonce; } static get genesis() { return new Block( 0, "0", 1508270000000, "Welcome to Blockchain Demo 2.0!", "000dc75a315c77a1f9c98fb6247d03dd18ac52632d7dc6a9920261d8109b37cf", 604 ); } } module.exports = Block; ================================================ FILE: Blockchain.js ================================================ const Block = require("./Block.js"); const crypto = require("crypto"); class Blockchain { constructor() { this.blockchain = [Block.genesis]; this.difficulty = 3; } get() { return this.blockchain; } get latestBlock() { return this.blockchain[this.blockchain.length - 1]; } isValidHashDifficulty(hash) { for (var i = 0; i < hash.length; i++) { if (hash[i] !== "0") { break; } } return i >= this.difficulty; } calculateHashForBlock(block) { const { index, previousHash, timestamp, data, nonce } = block; return this.calculateHash( index, previousHash, timestamp, data, nonce ); } calculateHash(index, previousHash, timestamp, data, nonce) { return crypto .createHash("sha256") .update(index + previousHash + timestamp + data + nonce) .digest("hex"); } mine(data) { const newBlock = this.generateNextBlock(data); try { this.addBlock(newBlock); } catch(err) { throw err; } } generateNextBlock(data) { const nextIndex = this.latestBlock.index + 1; const previousHash = this.latestBlock.hash; let timestamp = new Date().getTime(); let nonce = 0; let nextHash = this.calculateHash( nextIndex, previousHash, timestamp, data, nonce ); while (!this.isValidHashDifficulty(nextHash)) { nonce = nonce + 1; timestamp = new Date().getTime(); nextHash = this.calculateHash( nextIndex, previousHash, timestamp, data, nonce ); } const nextBlock = new Block( nextIndex, previousHash, timestamp, data, nextHash, nonce ); return nextBlock; } addBlock(newBlock) { if (this.isValidNextBlock(newBlock, this.latestBlock)) { this.blockchain.push(newBlock); } else { throw "Error: Invalid block"; } } isValidNextBlock(nextBlock, previousBlock) { const nextBlockHash = this.calculateHashForBlock(nextBlock); if (previousBlock.index + 1 !== nextBlock.index) { return false; } else if (previousBlock.hash !== nextBlock.previousHash) { return false; } else if (nextBlockHash !== nextBlock.hash) { return false; } else if (!this.isValidHashDifficulty(nextBlockHash)) { return false; } else { return true; } } isValidChain(chain) { if (JSON.stringify(chain[0]) !== JSON.stringify(Block.genesis)) { return false; } const tempChain = [chain[0]]; for (let i = 1; i < chain.length; i = i + 1) { if (this.isValidNextBlock(chain[i], tempChain[i - 1])) { tempChain.push(chain[i]); } else { return false; } } return true; } isChainLonger(chain) { return chain.length > this.blockchain.length; } replaceChain(newChain) { if (this.isValidChain(newChain) && this.isChainLonger(newChain)) { this.blockchain = JSON.parse(JSON.stringify(newChain)); } else { throw "Error: invalid chain"; } } } module.exports = Blockchain; ================================================ FILE: LICENSE.ISC.txt ================================================ ISC License Copyright (c) 2017, Sean Han Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ================================================ FILE: Messages.js ================================================ const messageType = require("./message-type.js"); const { REQUEST_LATEST_BLOCK, RECEIVE_LATEST_BLOCK, REQUEST_BLOCKCHAIN, RECEIVE_BLOCKCHAIN, REQUEST_TRANSACTIONS, RECEIVE_TRANSACTIONS } = messageType; class Messages { static getLatestBlock() { return { type: REQUEST_LATEST_BLOCK }; } static sendLatestBlock(block) { return { type: RECEIVE_LATEST_BLOCK, data: block }; } static getBlockchain() { return { type: REQUEST_BLOCKCHAIN }; } static sendBlockchain(blockchain) { return { type: RECEIVE_BLOCKCHAIN, data: blockchain }; } } module.exports = Messages; ================================================ FILE: P2p.js ================================================ const wrtc = require("wrtc"); const Exchange = require("peer-exchange"); const p2p = new Exchange("Blockchain Demo 2.0", { wrtc: wrtc }); const net = require("net"); const messageType = require("./message-type.js"); const { REQUEST_LATEST_BLOCK, RECEIVE_LATEST_BLOCK, REQUEST_BLOCKCHAIN, RECEIVE_BLOCKCHAIN, REQUEST_TRANSACTIONS, RECEIVE_TRANSACTIONS } = messageType; const Messages = require("./Messages.js"); class PeerToPeer { constructor(blockchain) { this.peers = []; this.blockchain = blockchain; } startServer(port) { const server = net .createServer(socket => p2p.accept(socket, (err, conn) => { if (err) { throw err; } else { this.initConnection.call(this, conn); } }) ) .listen(port); } discoverPeers() { p2p.getNewPeer((err, conn) => { if (err) { throw err; } else { this.initConnection.call(this, conn); } }); } connectToPeer(host, port) { const socket = net.connect(port, host, () => p2p.connect(socket, (err, conn) => { if (err) { throw err; } else { this.initConnection.call(this, conn); } }) ); } closeConnection() { p2p.close(err => { throw err; }); } broadcastLatest() { this.broadcast(Messages.sendLatestBlock(this.blockchain.latestBlock)); } broadcast(message) { this.peers.forEach(peer => this.write(peer, message)); } write(peer, message) { peer.write(JSON.stringify(message)); } initConnection(connection) { this.peers.push(connection); this.initMessageHandler(connection); this.initErrorHandler(connection); this.write(connection, Messages.getLatestBlock()); } initMessageHandler(connection) { connection.on("data", data => { const message = JSON.parse(data.toString("utf8")); this.handleMessage(connection, message); }); } initErrorHandler(connection) { connection.on("error", err => { throw err; }); } handleMessage(peer, message) { switch (message.type) { case REQUEST_LATEST_BLOCK: this.write(peer, Messages.sendLatestBlock(this.blockchain.latestBlock)); break; case REQUEST_BLOCKCHAIN: this.write(peer, Messages.sendBlockchain(this.blockchain.get())); break; case RECEIVE_LATEST_BLOCK: this.handleReceivedLatestBlock(message, peer); break; case RECEIVE_BLOCKCHAIN: this.handleReceivedBlockchain(message); break; default: throw "Received invalid message."; } } handleReceivedLatestBlock(message, peer) { const receivedBlock = message.data; const latestBlock = this.blockchain.latestBlock; if (latestBlock.hash === receivedBlock.previousHash) { try { this.blockchain.addBlock(receivedBlock); } catch(err) { throw err; } } else if (receivedBlock.index > latestBlock.index) { this.write(peer, Messages.getBlockchain()); } else { // Do nothing. } } handleReceivedBlockchain(message) { const receivedChain = message.data; try { this.blockchain.replaceChain(receivedChain); } catch(err) { throw err; } } } module.exports = PeerToPeer; ================================================ FILE: README.md ================================================