Skip to content

Commit

Permalink
added solutions for all except incentives
Browse files Browse the repository at this point in the history
  • Loading branch information
cooganb committed May 4, 2018
1 parent 451d72c commit 8177811
Show file tree
Hide file tree
Showing 13 changed files with 856 additions and 0 deletions.
39 changes: 39 additions & 0 deletions solutions/hashing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Bitcoin Whitepaper Exercises - Hashing

In this exercise, you will practice writing code to create blocks, compute block hashes, and verify blocks based on those hashes.

## Part 1

You are provided lines of text from a poem, and you should loop through them and add each line as its own block of data to the provided blockchain.

Define a `createBlock(..)` function which takes the text for its data, creates an object for the block, and computes its hash, finally returning the block object. Insert this object into the `blocks` array for the blockchain.

Each block should have the following fields:

* `index`: an incrementing number that's the 0-based position of the new block within the `blocks` array; the genesis block has `index` of `0`, so the next block will have `index` of `1`, and so on

* `prevHash`: the value of the `hash` field from the last block in the `blocks` array

* `data`: the string value passed into `createBlock(..)`

* `timestamp`: the numeric timestamp (from `Date.now()`) of the moment the block is created

* `hash`: the SHA256 hash of the block's other fields (`index`, `prevHash`, `data`, and `timestamp`)

Verify that your blockchain includes all 8 lines of the poem, each as separate blocks, for a total of 9 blocks including the genesis block.

## Part 2

Define a `verifyChain(..)` function that checks all blocks in the chain to ensure the chain is valid, and returns `true` or `false` accordingly. It may be useful to define a `verifyBlock(..)` function that can be called for each block object.

Each block should be checked for the following:

* `data` must be non-empty
* for the genesis block only, the hash must be `"000000"`
* `prevHash` must be non-empty
* `index` must be an integer >= `0`
* the `hash` must match what recomputing the hash with `blockHash(..)` produces

In addition to verifying a block, the linkage between one block and its previous block must be checked, throughout the whole chain. That is, the block at position 4 needs to have a `prevHash` equal to the `hash` of the block at position `3`, and so on.

Print out verification that the blockchain is valid after having added all the poem text as blocks.
89 changes: 89 additions & 0 deletions solutions/hashing/hashing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"use strict";

var crypto = require("crypto");

// The Power of a Smile
// by Tupac Shakur
var poem = [
"The power of a gun can kill",
"and the power of fire can burn",
"the power of wind can chill",
"and the power of a mind can learn",
"the power of anger can rage",
"inside until it tears u apart",
"but the power of a smile",
"especially yours can heal a frozen heart",
];

var Blockchain = {
blocks: [],
};

// Genesis block
Blockchain.blocks.push({
index: 0,
hash: "000000",
data: "",
timestamp: Date.now(),
});

for (let line of poem) {
Blockchain.blocks.push(
createBlock(line)
);
}

console.log(`Blockchain is valid: ${verifyChain(Blockchain)}`);


// **********************************

function createBlock(data) {
var bl = {
index: Blockchain.blocks.length,
prevHash: Blockchain.blocks[Blockchain.blocks.length-1].hash,
data,
timestamp: Date.now(),
};

bl.hash = blockHash(bl);

return bl;
}

function blockHash(bl) {
return crypto.createHash("sha256").update(
`${bl.index};${bl.prevHash};${JSON.stringify(bl.data)};${bl.timestamp}`
).digest("hex");
}

function verifyBlock(bl) {
if (bl.data == null) return false;
if (bl.index === 0) {
if (bl.hash !== "000000") return false;
}
else {
if (!bl.prevHash) return false;
if (!(
typeof bl.index === "number" &&
Number.isInteger(bl.index) &&
bl.index > 0
)) {
return false;
}
if (bl.hash !== blockHash(bl)) return false;
}

return true;
}

function verifyChain(chain) {
var prevHash;
for (let bl of chain.blocks) {
if (prevHash && bl.prevHash !== prevHash) return false;
if (!verifyBlock(bl)) return false;
prevHash = bl.hash;
}

return true;
}
19 changes: 19 additions & 0 deletions solutions/pow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Bitcoin Whitepaper Exercises - Proof of Work

In this exercise, we're going back to the simplicity of the first exercise; we're just adding a piece of text (a line from the poem) to a block, no transactions or signatures or inputs/outputs or any of that complication.

Instead, this exercise focuses on a simple implementation of the "Proof of Work" consensus algorithm -- the goal of which being to make it "difficult" / "not worth it" for a malicious person to recompute all the hashes in an entire chain just so they can rewrite a bit of history.

To implement Proof of Work, we're going to keep trying to compute a hash for a block until the hash is "lower" than a certain threshold, as defined by an incrementing `difficulty` integer. This `difficulty` value represents the required number of leading (on the left) `0`'s in the **binary representation** of the hash -- not the normal hexadecimal represtation where each character represents 4 bits. The more leading `0`'s there are, the lower that hash value is.

To get a new hash each time you compute, you need to change the data in the block. For this reason, blocks need a `nonce` field added, which is simply a random number. Each time you generate a new `nonce`, recompute the hash and compare (with `hashIsLowEnough(..)`) to see if it's low enough to be accepted for the current `difficulty`.

There are different ways of comparing a hash value's binary bit representation to see if it's low enough. Here are some hints to get you started:

* You don't need to compare the whole hash, only the first X hexadecimal digits (characters) of it from the left, depending on how many bits the `difficulty` value implies. Remember, 4 bits is one hexadecimal digit character.

* You can create a number value from a string representing its binary bits like this: `Number("0b001011011")`, which produces the number value `91`.

* `difficulty` means how many leading `0`'s must be present when representing a hash's left-most characters in binary. You may do this comparison based on string characters or numeric values (of either binary or base-10 form), whichever seems best to you. But, make sure you compare values in the same kind of representation/base.

* JavaScript now supports a `padStart(..)` utility for strings, which may be useful here.
105 changes: 105 additions & 0 deletions solutions/pow/pow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"use strict";

var crypto = require("crypto");

// The Power of a Smile
// by Tupac Shakur
var poem = [
"The power of a gun can kill",
"and the power of fire can burn",
"the power of wind can chill",
"and the power of a mind can learn",
"the power of anger can rage",
"inside until it tears u apart",
"but the power of a smile",
"especially yours can heal a frozen heart",
];

var difficulty = 10;

var Blockchain = {
blocks: [],
};

// Genesis block
Blockchain.blocks.push({
index: 0,
hash: "000000",
data: "",
timestamp: Date.now(),
});

for (let line of poem) {
let bl = createBlock(line);
Blockchain.blocks.push(bl);
console.log(`Hash (Difficulty: ${difficulty}): ${bl.hash}`);

difficulty++;
}


// **********************************

function createBlock(data) {
var bl = {
index: Blockchain.blocks.length,
prevHash: Blockchain.blocks[Blockchain.blocks.length-1].hash,
data,
timestamp: Date.now(),
};

bl.hash = blockHash(bl);

return bl;
}

function blockHash(bl) {
while (true) {
bl.nonce = Math.trunc(Math.random() * 1E7);
let hash = crypto.createHash("sha256").update(
`${bl.index};${bl.prevHash};${JSON.stringify(bl.data)};${bl.timestamp};${bl.nonce}`
).digest("hex");

if (hashIsLowEnough(hash)) {
return hash;
}
}
}

function hashIsLowEnough(hash) {
var neededChars = Math.ceil(difficulty / 4);
var threshold = Number(`0b${"".padStart(neededChars * 4,"1111".padStart(4 + difficulty,"0"))}`);
var prefix = Number(`0x${hash.substr(0,neededChars)}`);
return prefix <= threshold;
}

function verifyBlock(bl) {
if (bl.data == null) return false;
if (bl.index === 0) {
if (bl.hash !== "000000") return false;
}
else {
if (!bl.prevHash) return false;
if (!(
typeof bl.index === "number" &&
Number.isInteger(bl.index) &&
bl.index > 0
)) {
return false;
}
if (bl.hash !== blockHash(bl)) return false;
}

return true;
}

function verifyChain(chain) {
var prevHash;
for (let bl of chain.blocks) {
if (prevHash && bl.prevHash !== prevHash) return false;
if (!verifyBlock(bl)) return false;
prevHash = bl.hash;
}

return true;
}
31 changes: 31 additions & 0 deletions solutions/transactions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Bitcoin Whitepaper Exercises - Transactions

Similar to the previous Hashing exercise, in this exercise you will add the 8 lines of text from the provided poem to a blockchain. But this time, you will add each one as a separate transaction, authorize the transaction with a key and signature, and then add all 8 transactions to a single block. Finally, you'll validate the blockchain, including all the transaction signatures.

## Setup

Run `npm install` in this exercise folder to install the dependencies listed in the included `package.json` file.

Run `node generate-keys.js` from the command-line to create the `keys/` sub-directory which includes a keypair to use for digital signatures.

## Part 1

Define a `createTransaction(..)` function that takes the text (line of a poem) and creates a transaction object. The transaction object should have a `data`. The transaction then needs a `hash` field with the value returned from `transactionHash(..)`.

Define an asynchronous `authorizeTransaction(..)` function which then adds to the transaction object, a `pubKey` field with the public key text (`PUB_KEY_TEXT`), and a `signature` field with the signature created by `await`ing a call to `createSignature(..)`.

In `addPoem()`, for each line of the poem, create and authorize a transaction object and store it in the `transactions` array. Then add set those transactions as the data for a new block, and insert the block into the blockchain.

## Part 2

Modify `verifyBlock(..)` to validate all the transactions in the `data` of a block. It may be useful to define a `verifyTransaction(..)` function.

Each transaction should be verified according to:

* `hash` should match what's computed with `transactionHash(..)`

* should include `pubKey` string and `signature` string fields

* the `signature` should verify correctly with `verifySignature(..)`

Print out verification that the blockchain is valid after having added all the poem text as transactions.
22 changes: 22 additions & 0 deletions solutions/transactions/generate-keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use strict";

var path = require("path");
var fs = require("fs");
var openpgp = require("openpgp");

const KEYS_DIR = path.join(__dirname,"keys");

var options = {
userIds: [{ name: "Bitcoin Whitepaper", email: "[email protected]" }],
numBits: 2048,
passphrase: "",
};

openpgp.generateKey(options).then(function onGenerated(key) {
try { fs.mkdirSync(KEYS_DIR); } catch (err) {}

fs.writeFileSync(path.join(KEYS_DIR,"priv.pgp.key"),key.privateKeyArmored,"utf8");
fs.writeFileSync(path.join(KEYS_DIR,"pub.pgp.key"),key.publicKeyArmored,"utf8");

console.log("Keypair generated.");
});
7 changes: 7 additions & 0 deletions solutions/transactions/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "solutions-transactions",
"main": "transactions.js",
"dependencies": {
"openpgp": "~3.0.8"
}
}
Loading

0 comments on commit 8177811

Please sign in to comment.