Do you want to build a blockchain?

James Allardice

orangejellyfish

James Allardice

> git clone git@github.com:orangejellyfish/jellycoin
> git checkout boilerplate

James Allardice

James Allardice

index
0

James Allardice

index
timestamp
0
1536846506393

James Allardice

index
timestamp
0
1536846506393
data
foo

James Allardice

export default class Block {
  constructor(index, data) {
    this.index = index;
    this.data = JSON.stringify(data);
    this.timestamp = new Date().getTime();
  }
}

James Allardice

index
timestamp
0
1536846506393
data
foo
hash
2c26b46b6...

James Allardice

f(x) = y

James Allardice

f(x) = y

digest

"a"   = ca978112ca1bbdcafac231b39a23dc4da7...

James Allardice

f(x) = y
"foo" = 2c26b46b68ffc68ff99b453c1d30413413...

64 characters

"a"   = ca978112ca1bbdcafac231b39a23dc4da7...

64 characters

James Allardice

f(x) = y
"foo" = 2c26b46b68ffc68ff99b453c1d30413413...
"Foo" = 1cbec737f863e4922cee63cc2ebbfaafcd...

64 characters

"a"   = ca978112ca1bbdcafac231b39a23dc4da7...

64 characters

64 characters

James Allardice

import crypto from 'crypto';

export default class Block {
  constructor(index, data) {
    this.index = index;
    this.data = JSON.stringify(data);
    this.timestamp = new Date().getTime();
    this.hash = this.createHash();
  }

  createHash() {
    return crypto
      .createHash('sha256')
      .update(this.index + this.data + this.timestamp)
      .digest('hex');
  }
}

James Allardice

index
timestamp
1
153716836841
data
bar
hash
fcde2b2ed...
previousHash
2c26b46b6...

James Allardice

index
timestamp
1
153716836841
data
bar
hash
fcde2b2ed...
previousHash
2c26b46b6...
index
timestamp
0
1536846506393
data
foo
hash
2c26b46b6...

James Allardice

alice pays bob 100
charlie pays bob 50
dan pays alice 250
4f013941...
ea49c9e5...
0491b0a5...
00000000...
4f013941...
ea49c9e5...

James Allardice

alice pays eve 100
charlie pays bob 50
dan pays alice 250
4f013941...
ea49c9e5...
0491b0a5...
00000000...
4f013941...
ea49c9e5...

James Allardice

alice pays eve 100
charlie pays bob 50
dan pays alice 250
aa190473...
ea49c9e5...
0491b0a5...
00000000...
4f013941...
ea49c9e5...

James Allardice

alice pays eve 100
charlie pays bob 50
dan pays alice 250
aa190473...
ea49c9e5...
0491b0a5...
00000000...
aa190473...
ea49c9e5...

James Allardice

import crypto from 'crypto';

export default class Block {
  constructor(index, data, previousHash) {
    this.index = index;
    this.data = JSON.stringify(data);
    this.timestamp = new Date().getTime();
    this.previousHash = previousHash;
    this.hash = this.createHash();
  }

  createHash() {
    return crypto
      .createHash('sha256')
      .update(this.index + this.data + this.timestamp + this.previousHash)
      .digest('hex');
  }
}

James Allardice

export default class Transaction {
  constructor(from, to, amount) {
    this.from = from;
    this.to = to;
    this.amount = amount;
  }
}

James Allardice

import Block from '../block';
import Transaction from '../transaction';

export default class Blockchain {
  constructor(blockchain) {
    if (blockchain) {
      this.chain = blockchain.chain;
    } else {
      this.chain = [];
      this.createGenesisBlock();
    }
  }

  createGenesisBlock() {
    const genesisTransaction = new Transaction(null, 'alice', 50);
    const genesisBlock = new Block(0, genesisTransaction, '0'.repeat(64));

    this.chain.push(genesisBlock);
  }
}

James Allardice

const Block = require('../block');

class Blockchain {
  // ...

  createBlock(data) {
    const previousBlock = this.chain[this.chain.length - 1];
    const nextIndex = previousBlock.index + 1;
    const nextBlock = new Block(nextIndex, data, previousBlock.hash);

    this.chain.push(nextBlock);

    return nextBlock;
  }
}

James Allardice

const Block = require('../block');

class Blockchain {
  // ...

  validateBlock(block) {
    const previousBlock = this.getLatestBlock();

    return block.previousHash === previousBlock.hash
      && block.index === previousBlock.index + 1;
  }   
}

James Allardice

import Blockchain from './blockchain';

const chain = new Blockchain();

James Allardice

server.get('/blockchain', (req, res) => {
  res.json(chain);
});

James Allardice

server.post('/block', (req, res) => {
    console.log('\nCreating new block...');

    const nextBlock = chain.createBlock(
      new Transaction(req.body.from, req.body.to, req.body.amount),
    );

    return res.sendStatus(201);
  });

James Allardice

> curl localhost:1234/blockchain

James Allardice

> curl -XPOST -H 'Content-Type: application/json' -d '{"from": "x", "to": "y", "amount": 100 }' -i localhost:1234/block

James Allardice

Proof of work

James Allardice

"a"           = ca978112ca1bbdcafac231b39a...

Proof of work

James Allardice

"a"           = ca978112ca1bbdcafac231b39a...
                00000...

Proof of work

James Allardice

"a"           = ca978112ca1bbdcafac231b39a...
                00000...
"a" + 1       = f55ff16f66f43360266b95db6f...
                00000...

Proof of work

James Allardice

"a"           = ca978112ca1bbdcafac231b39a...
                00000...
"a" + 1       = f55ff16f66f43360266b95db6f...
                00000...
"a" + 2       = 2c3a4249d77070058649dbd822...
                00000...

Proof of work

James Allardice

"a" + 862181  = f2c0efd549955f6bee06bb4266...
                00000...
"a" + 862182  = e6d42d1554bfa7a813adfa0784...
                00000...
"a" + 862183  = 000008a7d97881343ade2a4fa7...
                00000...

Proof of work

James Allardice

export default class Block {
  constructor(index, data, previousHash) {
    // ...
    this.nonce = 0;
  }

  // ...

  findNonce(difficulty) {
    const zeroes = '0'.repeat(difficulty);

    while (!this.hash.startsWith(zeroes)) {
      this.nonce++;
      this.hash = this.createHash();
    }
  }
};

James Allardice

export default class Blockchain {
  // ...

  createBlock(data) {
    const previousBlock = this.chain[this.chain.length - 1];
    const nextIndex = previousBlock.index + 1;
    const nextBlock = new Block(nextIndex, data, previousBlock.hash);

    nextBlock.findNonce(this.difficulty);
    this.chain.push(nextBlock);
  }
};

James Allardice

P2P

James Allardice

P2P

Find the longest chain

James Allardice

P2P

Find the longest chain

Broadcast new blocks

James Allardice

P2P

Find the longest chain

Broadcast new blocks

Receive and validate new blocks

James Allardice

function handleReceiveBlock(peer, packet) {
  // A peer has created a new block. We need to validate the block in
  // its own right to ensure the peer has got the correct proof-of-work
  // nonce. We also need to check whether we can append the block to the
  // chain we currently hold. If our chain is more than one block behind
  // we'll have to ask peers to send their latest version.
  const newBlock = new Block(packet.data);

  console.log('\nReceived new block...');
  if (newBlock.isValid()) {
    console.log('New block is valid...');

    if (chain.validateBlock(newBlock)) {
      console.log('New block is valid for our current chain... appending');
      return chain.append(newBlock);
    }

    console.log('New block is invalid for our current chain...');
    return broadcast({ message: 'GET_BLOCKCHAIN' });
  }

  console.log('New block is invalid... ignoring');
}

James Allardice

function handleGetBlockchain(peer) {
  console.log('\nPeer requested full chain... sending');
  message(peer, { message: 'BLOCKCHAIN', data: chain });
}

James Allardice

function handleReceiveBlockchain(peer, packet) {
  // A peer has sent us their current copy of the chain. If theirs is
  // longer than ours we can replace ours.
  //
  // TODO: validate the received chain in full.
  //
  console.log('\nReceived chain from peer...');
  const newChain = new Blockchain(packet.data);

  if (newChain.getLatestBlock().index > chain.getLatestBlock().index) {
    console.log('Received chain is longer... replacing');
    chain = newChain;
  } else {
    console.log('Received chain is not longer... ignoring');
  }
}

James Allardice

James Allardice

What next?

Grouping many transactions into a block

Dynamic difficulty to ensure uniform block mining intervals

Public/private keys, addresses and signing

James Allardice

Thank you!

James Allardice

orangejellyfish

Code: https://github.com/orangejellyfish/jellycoin

Slides: https://slides.com/jamesallardice/do-you-want-to-build-a-blockchain-devconf18