Skip to content

Instantly share code, notes, and snippets.

@tylerdiaz
Created November 27, 2017 08:19
Show Gist options
  • Save tylerdiaz/8a44e7acc6273de6197e9ebf8c5e4ba7 to your computer and use it in GitHub Desktop.
Save tylerdiaz/8a44e7acc6273de6197e9ebf8c5e4ba7 to your computer and use it in GitHub Desktop.
blockchain
const RushaClass = require('Rusha');
const rusha = new RushaClass();
const CONFIG = { difficulty: 3 }
// from: https://bitcoin.org/bitcoin.pdf
// and: https://www.igvita.com/2014/05/05/minimum-viable-block-chain/
// I do sha1 instead of sha256 here for performance
class Block {
constructor({ prevHash = 0, timestamp = null, nonce = 0, body = "", hash = "", height = 0 }) {
this.prevHash = prevHash
this.nonce = nonce
this.body = body
this.hash = hash
this.height = height
this.timestamp = (timestamp || new Date().getTime())
}
static fromString(blockString){
return new Block(JSON.parse(blockString))
}
difficulty() {
return this.hash.search(/[1-9a-zA-Z]/) // serch for none 0
}
get data() {
return {
prevHash: this.prevHash,
nonce: this.nonce,
height: this.height,
timestamp: this.timestamp,
body: this.body
}
}
generateHash() {
this.hash = rusha.digestFromString(this.toString())
return this.hash
}
mine() {
let attempts = 0;
while(!this.isValid()){
this.nonce = rusha.digestFromString(Math.random().toString())
this.generateHash();
attempts++
}
// console.log(`Mined "${this.body}" block after ${attempts} tries`)
return { attempts }
}
isValid() {
return this.difficulty() >= CONFIG.difficulty
}
toString() {
return JSON.stringify({ ...this.data, hash: this.hash })
}
}
class Blockchain {
constructor(blocks) {
this.blocks = blocks
}
static fromString(blockchain_dump) {
return blockchain_dump.split("\n")
.map(b => { return Block.fromString(b) })
}
length() {
return this.blocks.length
}
toString() {
return this.blocks.map(b => { return b.toString() })
.join("\n")
}
// review the entire blockchain and ensure it's valid
mine() {
for (var i = 0; i < this.blocks.length; i++) {
if (i == 0) continue // genesis
let currentBlock = this.blocks[i]
let prevBlock = this.blocks[i - 1]
if (currentBlock.prevHash != prevBlock.hash) {
console.log(`correcting block sequence to ${prevBlock.body}->${currentBlock.body}`)
currentBlock.prevHash = prevBlock.hash
}
currentBlock.mine()
}
}
// this function might invalidate the blockchain
append(block) {
let tailBlock = this.blocks[this.blocks.length - 1]
block.prevHash = tailBlock.hash
block.height = this.blocks.length
if (! block.isValid()) block.mine()
this.blocks.push(block)
return this
}
}
let genesisBlock = new Block({
prevHash: "0",
nonce: "919ddb02165d3c490133e479d1955b08ad3ab0cb",
hash: "000cc64a0037f5a29d4f18736d201c7cd38c696c",
height: 0,
timestamp: 1,
body: "The Genesis block"
})
let blockchain = new Blockchain([ genesisBlock ])
blockchain.append(new Block({ body: "An extension block" }))
blockchain.append(new Block({ body: "maybe an action in here?" }))
blockchain.append(new Block({ body: "then a reaction in here" }))
blockchain.append(new Block({ body: "one last block" }))
blockchain.mine()
console.log(
`Blockchain started`,
blockchain
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment