L2 Proof of Work

Proof of work

You want to limit block generation so that nobody can fork the blockchain by just instamining a bunch of blocks. This wouldn’t matter on private blockchains where you trust all the miners. There, you’d either want a private network, or you’d only want to accept block hashes with signatures from one of your trusted miners.

 

Minicoin’s block difficulty measurement is the same as Bitcoin’s. A block hash has 256 bits. When you hash a block, each bit will be randomly 1 or 0. The number of 0 bits at the start of the hash is its difficulty. There’s a 50% chance that a random hash will have a difficulty of 1. There’s a 25% chance it will have a difficulty of 2. It will be ½ as likely to meet each increasing level of difficulty.

 

If a block’s difficulty isn’t high enough, how do you make a different hash but keep the same block time and transactions? Introduce a nonce property to the unhashed block. It’s an arbitrary integer that you can twiddle each time you want a different hash. You can generate a ton of nearly-identical blocks with different hashes until you get one difficult enough to be accepted onto the blockchain.

 

What’s the point of difficulty? To slow down block generation time. Minicoin’s reference code has a difficulty requirement algorithm with a target block interval of 10 seconds.

 

  • If a block takes 5-20 seconds, the next block’s difficulty must be the same.
  • If it’s made too fast, bump the required difficulty +1 for the next block.
  • If it’s made too slow, lower the difficulty.
  • Limit the difficulty to the range of 0 to 256.

 

Minicoin’s difficulty adjustment algorithm is a cruder version of Bitcoin’s. Bitcoin takes the average interval of the previous 2016 blocks before re-adjusting the difficulty[1], so you can expect to have more fluctuations.

 

Make a working copy of L2/stubs to start with the reference code, or add to your own code from L1.

main.js

Note that the .mine command now waits only 50 ms before trying to mine a block. It checks if the miner successfully made a good new block to add to the blockchain, before adding and broadcasting it.

 

There is also an .intervals command to check the block time and difficulty statistics.

work.js

This new module implements the difficulty algorithm.

 

nextDifficulty(chain) should take a blockchain and return the target number of leading zeros required in the next block’s hash. You can just do the entire difficulty walk and it will be fast enough for testing purposes.

 

blockDifficulty(block) should return the actual difficulty of a hashed block.

 

chainDifficulty(chain) should return the actual cumulative difficulty of an entire blockchain. It will let you determine which blockchain fork to use.

 

Unfortunately, you can’t just add the powers and you must do the full exponentiation and addition, i.e. 2^(block 1 difficulty) + 2^(block 2 difficulty)… It will be a big 64-bit integer. Note the limitation that JS Number can only reliably handle integers below 2^53. You might have problems if your block difficulties get up to 2^90, like Bitcoin!

miner.js

mineBlock(block) should now add a block nonce. It should return a good new block or else false if the block isn’t difficult enough (so main can wait 50 ms to try again).

blocks.js

addBlock(block, chain) should now validate a block’s nonce and difficulty before adding it to the referenced chain.

 

swapChains(chain) should now swap to the fork if it’s cumulative difficulty is greater than the local blockchain.

 

The P2P module is effectively unchanged. Try improving your interval statistics by tweaking the difficulty adjustment algorithm. Perhaps a new block’s difficulty should equal the required difficulty instead of being equal or greater than. Does increasing the hash rate, adding averaging, or increasing the target interval help?

 

[1] See https://en.bitcoin.it/wiki/Difficulty.