//STYLE
document.write(`
<style>
.previous, .hash{
font-family: monospace;
}
.block{
margin: 8px;
border: 1px solid #000;
border-radius: 1px;
}
</style>
`);
//TEMPLATE
document.write('<div id="blockchain"></div>');
document.write('<div id="users"></div>');
$('#ab-wrapper').css('height', '0px');
//
// Import SHA256 lib
// https://npm.io/package/js-sha256
$.getScript('https://cdnjs.cloudflare.com/ajax/libs/js-sha256/0.9.0/sha256.min.js',
() => console.log('SHA256 Support Loaded'));
//hash values must to begin with as much 0 as defined in difficulty
const difficulty = 3;
class Block{
constructor(id, timestamp, transaction, previous, complexity){
this.id = id;
this.timestamp = timestamp;
this.transaction = transaction;
this.nonce = 0;
this.previous = previous;
this.complexity = complexity;
this.hash = 0;//this.mine();
}
generateHash(){
return sha256(JSON.stringify(this));
}
mine(){
let h = this.generateHash();
while (h.substr(0, this.complexity) != new Array(this.complexity).fill(0).join('')) {
this.nonce += 1;
h = this.generateHash();
}
this.hash = h;
}
getHtml() {
return `
<table class="block" id="`+this.id+`">
<tr class="id"><td>id:</td><td>`+this.id+`</td></tr>
<tr class="previous"><th>Previous: </th><td>`+this.formatHashCode(this.previous)+`</td>
<tr class="timestamp"><td>timestamp: </td><td>`+this.timestamp+`</td></tr>
<tr class="transaction"><td>transaction: </td><td>`+this.transaction.getHtml()+`</td></tr>
<tr class="nonce"><td>nonce: </td><td>`+this.nonce+`</td></tr>
<tr class="hash"><td>hash: </td><td>`+this.formatHashCode(String(this.hash))+`</td></tr>
<tr><th>Mine: </th> <td><button onclick="mine(`+this.id+`)">Mine</button></td></tr>
</table>
`;
}
formatHashCode(code) {
return code.substring(0, 32) + '<br>' + code.substring(32, 64);
}
}
class Transaction{
constructor(from, to, amount) {
this.from = from;
this.to = to;
this.amount = amount;
}
getHtml() {
return `
<div class="from">from: `+this.from+`</div>
<div class="from">to: `+this.to+`</div>
<div class="from">amount: `+this.amount+`</div>
`;
}
}
class BlockChain{
constructor(complexity){
this.complexity = complexity;
this.chain = [this.generateGenesisBlock()];
this.pendingTransaction = null;
}
generateGenesisBlock(){
return new Block(0, Date.now(), new Transaction('System', 'System', 0),
new Array(64).fill(0).join(''), this.complexity);
}
minePendingTransaction(){
let block = new Block(this.chain[this.chain.length - 1].id + 1, Date.now(), this.pendingTransaction, this.chain[this.chain.length-1].hash, this.complexity);
this.chain.push(block);
}
createTransaction(transaction){
this.pendingTransaction = transaction;
}
getAddressBalance(address){
balance = 0;
for (let block of this.chain) {
if (address == transaction.from)
balance -= transaction.amount;
if (address == transaction.to)
balance += transaction.amount;
}
return balance;
}
isValid(){
for (let i = 1; i < this.chain.length; i++) {
if (!this.isBlockValid(i)) return false;
}
return true;
}
isBlockValid(i){
this.calculateHash(i);
return this.chain[i].hash.substr(0, this.complexity) == new Array(this.complexity).fill(0).join('');
}
calculateHash(i) {
this.chain[i].previous = this.chain[i-1].generateHash();
this.chain[i].hash = this.chain[i].generateHash();
}
}
function mine(id) {
console.log('MINING', id);
bc.chain[id].mine();
bc.isValid();
drawBlockchain(bc);
}
//---- test blockchain --------------------------------------------
let bc = new BlockChain(difficulty);
bc.createTransaction(new Transaction('System', 'Alice', 100));
bc.minePendingTransaction();
//---- draw -------------------------------------------------------
function drawBlockchain(bc) {
$('#blockchain').html('');
for(let i = 0; i < bc.chain.length; i++)
$('#blockchain').html($('#blockchain').html() + bc.chain[i].getHtml());
}
function setup() // "setup" is called once at start of run
{
}
drawBlockchain(bc);
function draw() // Optional
{
}