Created
November 29, 2017 14:29
-
-
Save mizchi/5d08bdbe96a77c129eb779053268ff20 to your computer and use it in GitHub Desktop.
Minimum blockchain implements: referred https://github.com/lhartikk/naivechain/blob/master/main.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* @flow */ | |
import SHA256 from 'crypto-js/sha256' | |
type Block = { | |
index: number, | |
previousHash: string, | |
timestamp: number, | |
data: string, | |
hash: string | |
} | |
type BlockChain = Block[] | |
/* initial block chain */ | |
export function getGenesisBlock(): Block { | |
return createBlock( | |
0, | |
'0', | |
1465154705, | |
'my genesis block!!', | |
'816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7' | |
) | |
} | |
export function getLatestBlock(blockchain: BlockChain): Block { | |
return blockchain[blockchain.length - 1] | |
} | |
/* implementation */ | |
export function createBlock( | |
index: number, | |
previousHash: string, | |
timestamp: number, | |
data: any, | |
hash: string | |
): Block { | |
return { | |
index, | |
previousHash, | |
timestamp, | |
data, | |
hash | |
} | |
} | |
export function calculateHashForBlock(block: Block) { | |
return calculateHash( | |
block.index, | |
block.previousHash, | |
block.timestamp, | |
block.data | |
) | |
} | |
export function calculateHash( | |
index: number, | |
previousHash: string, | |
timestamp: number, | |
data: string = '' | |
): string { | |
return SHA256(index + previousHash + timestamp + data).toString() | |
} | |
export function generateNextBlock( | |
blockchain: BlockChain, | |
blockData: string = '' | |
): Block { | |
const previousBlock = getLatestBlock(blockchain) | |
const nextIndex = previousBlock.index + 1 | |
const nextTimestamp = ~~(Date.now() / 1000) | |
const nextHash = calculateHash( | |
nextIndex, | |
previousBlock.hash, | |
nextTimestamp, | |
blockData | |
) | |
return { | |
index: nextIndex, | |
previousHash: previousBlock.hash, | |
timestamp: nextTimestamp, | |
data: blockData, | |
hash: nextHash | |
} | |
} | |
export function isValidNewBlock( | |
newBlock: Block, | |
previousBlock: Block | |
): boolean { | |
if (previousBlock.index + 1 !== newBlock.index) { | |
console.log('invalid index') | |
return false | |
} else if (previousBlock.hash !== newBlock.previousHash) { | |
console.log('invalid previoushash') | |
return false | |
} else if (calculateHashForBlock(newBlock) !== newBlock.hash) { | |
console.log( | |
'invalid hash: ' + calculateHashForBlock(newBlock) + ' ' + newBlock.hash | |
) | |
return false | |
} | |
return true | |
} | |
export function isValidChain(blockchainToValidate: BlockChain): boolean { | |
// check genesis block | |
if ( | |
JSON.stringify(blockchainToValidate[0]) !== | |
JSON.stringify(getGenesisBlock()) | |
) { | |
return false | |
} | |
const tempBlocks = [blockchainToValidate[0]] | |
for (var i = 1; i < blockchainToValidate.length; i++) { | |
if (isValidNewBlock(blockchainToValidate[i], tempBlocks[i - 1])) { | |
tempBlocks.push(blockchainToValidate[i]) | |
} else { | |
return false | |
} | |
} | |
return true | |
} | |
export function addBlock(blockchain: BlockChain, newBlock: Block): BlockChain { | |
if (isValidNewBlock(newBlock, getLatestBlock(blockchain))) { | |
return blockchain.concat([newBlock]) | |
} | |
return blockchain | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
miner1 checks receivedBlockchain. Size: 1 | |
miner1 add new block: 642ea53597d4d90eca7cbc9bcdd80217b76d89dae027fb70c9b881b56e9e7710 | |
miner1 add new block: 7eebe4fd8ef821553a8a62d5e85bce6515e954f8420401b6733dafea4c9f0673 | |
miner1 bloadcasted | |
miner3 checks receivedBlockchain. Size: 3 | |
miner3 accepted received blockchain | |
miner3 bloadcasted | |
miner1 checks receivedBlockchain. Size: 3 | |
miner1 add new block: 03cabfbaafaa9a381118cc665f505deb157ef4d49d2969f13749a6a0771e6587 | |
miner1 add new block: f8f903b8954dd8009bc276019b6cc446b31e87cae637f6ec2e99c7c99a496d9b | |
miner1 bloadcasted | |
miner2 checks receivedBlockchain. Size: 5 | |
miner2 accepted received blockchain | |
miner2 add new block: 1feac1e2d90e1252cc1bbb3bf6ec7cab4da25787cf64d03cf600d4c534309168 | |
miner2 add new block: c59e8fa1882dcb30b0ab43c55f0121460abc655883566789b510f59d8165d4b0 | |
miner2 bloadcasted | |
miner2 checks receivedBlockchain. Size: 7 | |
miner2 bloadcasted | |
miner3 checks receivedBlockchain. Size: 7 | |
miner3 accepted received blockchain | |
miner3 add new block: 6e4709bac242febc16827b6d2cf3f515295b74a61439640187418d68071435bf | |
miner3 add new block: 42ff0c9d57434a7fa444e886f8c3bb5db1bdd2216fb87a83bc141834f4533610 | |
miner3 bloadcasted | |
miner3 checks receivedBlockchain. Size: 9 | |
miner3 bloadcasted | |
miner1 checks receivedBlockchain. Size: 9 | |
miner1 accepted received blockchain | |
miner1 add new block: 0d1200472bf18a419a41fde1ecbb3715177e1e67f7152618840d84c2b6f91b61 | |
miner1 add new block: 4a88da04e21bd5aafa7e874fb048e1ffd28d32059b8e3dd2ee2c628ea19da266 | |
miner1 bloadcasted | |
miner3 checks receivedBlockchain. Size: 11 | |
miner3 accepted received blockchain | |
miner3 add new block: 4a82941ec34034446e11b0c0cbd618a420b4416d7e4a41625888a1005ec3a8b0 | |
miner3 add new block: 6f41d94dd78e7296bb396cd8f7193c9232c44bbf7f556ea9c23c588846ebc4ed | |
miner3 bloadcasted | |
miner2 checks receivedBlockchain. Size: 13 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* @flow */ | |
import { | |
getGenesisBlock, | |
generateNextBlock, | |
getLatestBlock, | |
addBlock, | |
isValidChain, | |
isValidNewBlock | |
} from './blockchain' | |
import range from 'lodash.range' | |
const blockchain = [getGenesisBlock()] | |
// basic use | |
const prev = getLatestBlock(blockchain) | |
const next1 = generateNextBlock(blockchain, 'foo') | |
const newBlockchain1 = addBlock(blockchain, next1) | |
const next2 = generateNextBlock(newBlockchain1, 'bar') | |
const newBlockchai2 = addBlock(newBlockchain1, next2) | |
console.log('current', newBlockchai2) | |
console.log('isValidChain', isValidChain(newBlockchai2)) | |
const wait = n => new Promise(resolve => setTimeout(resolve, n)) | |
// It should not be singleton but easy to test. | |
let receivedBlockchain = [getGenesisBlock()] | |
const bloadcast = (name, next) => { | |
console.log(`${name} bloadcasted`) | |
receivedBlockchain = next | |
} | |
const createMiner = (name: string) => { | |
let myBlockchain = blockchain.slice() | |
const accept = () => { | |
console.log( | |
`${name} checks receivedBlockchain. Size: ${receivedBlockchain.length}` | |
) | |
// console.log(receivedBlockchain.length, myBlockchain.length) | |
if (receivedBlockchain.length > myBlockchain.length) { | |
if (isValidChain(receivedBlockchain)) { | |
console.log(`${name} accepted received blockchain`) | |
myBlockchain = receivedBlockchain | |
} else { | |
console.log('Received blockchain invalid') | |
} | |
} | |
} | |
const addNewBlock = () => { | |
const next = generateNextBlock(myBlockchain) | |
myBlockchain = addBlock(myBlockchain, next) | |
console.log(`${name} add new block: ${next.hash}`) | |
} | |
return async () => { | |
while (true) { | |
await wait(Math.random() * 3000) | |
accept() | |
range(~~(Math.random() * 3)).forEach(_ => addNewBlock()) | |
bloadcast(name, myBlockchain) | |
} | |
} | |
} | |
// competitive miners | |
createMiner('miner1')() | |
createMiner('miner2')() | |
createMiner('miner3')() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment