Skip to content

Latest commit

 

History

History
185 lines (147 loc) · 5.07 KB

04_timestamping.md

File metadata and controls

185 lines (147 loc) · 5.07 KB

Timestamping

How can you make the blockchain even more secure? Link it to the real world! Let's add a timestamp:

Time.now
# => 2018-03-17 14:12:01 +0100

or in Epoch time (that is, seconds since January 1st, 1970)

Time.now.to_i
# => 1521292321

Note: You can use Time.at to convert Epoch time back to the standard "classic" format:

Time.at( 1521292321 )
# => 2018-03-17 14:12:01 +0100

Now the blockchain must always move forward, that is, you can only add a new block if the timestamp is bigger / younger than the previous block's timestamp.

Unbreakable. Unbreakable. Unbreakable. What else?

Let's add the proof-of-work difficulty (e.g. '00', '000', '0000' etc.) to the hash to make the difficulty unbreakable / unchangeable too!

Last but not least let's drop the "pre-calculated" hash attribute and let's always calculate the hash on demand e.g.:

def hash
  Digest::SHA256.hexdigest( "#{nonce}#{time}#{difficulty}#{prev}#{data}" )
end

Remember: Calculating the block's (crypto) hash is fast, fast, fast. What take's time depending on the proof-of-work difficulty is finding the nonce, that is, the lucky number used once.

All together now. Resulting in:

require 'digest'
require 'pp'      ## pp = pretty print


class Block
  attr_reader :data
  attr_reader :prev
  attr_reader :difficulty
  attr_reader :time
  attr_reader :nonce  # number used once - lucky (mining) lottery number

  def hash
    Digest::SHA256.hexdigest( "#{nonce}#{time}#{difficulty}#{prev}#{data}" )
  end

  def initialize(data, prev, difficulty: '0000' )
    @data         = data
    @prev         = prev
    @difficulty   = difficulty
    @nonce, @time = compute_hash_with_proof_of_work( difficulty )
  end

  def compute_hash_with_proof_of_work( difficulty='00' )
    nonce = 0
    time  = Time.now.to_i
    loop do
      hash = Digest::SHA256.hexdigest( "#{nonce}#{time}#{difficulty}#{prev}#{data}" )
      if hash.start_with?( difficulty )
        return [nonce,time]    ## bingo! proof of work if hash starts with leading zeros (00)
      else
        nonce += 1             ## keep trying (and trying and trying)
      end
    end # loop
  end # method compute_hash_with_proof_of_work

end # class Block

Proof of the pudding. Let's build a new (more secure) blockchain from scratch (zero). Genesis!

b0 = Block.new( 'Hello, Cryptos!', '0000000000000000000000000000000000000000000000000000000000000000' )
#=> #<Block:0x4d00700
#       @data="Hello, Cryptos!",
#       @difficulty="0000",
#       @nonce=215028,
#       @prev="0000000000000000000000000000000000000000000000000000000000000000",
#       @time=1521292321>

Let's mine (build) some more blocks linked (chained) together with crypto hashes:

b1 = Block.new( 'Hello, Cryptos! - Hello, Cryptos!', b0.hash )
#=> #<Block:0x4ed7940
#       @data="Hello, Cryptos! - Hello, Cryptos!",
#       @difficulty="0000",
#       @nonce=3264,
#       @prev="0000071b9c71675db90b0bb819236d76be97ac75f9f379d078456495133b18c6",
#       @time=1521292325>

b2 = Block.new( 'Your Name Here', b1.hash )
#=> #<Block:0x2f297e8
#       @data="Your Name Here",
#       @difficulty="0000",
#       @nonce=81552,
#       @prev="0000a6f83a7883891afea2536891df228a1c527add36c1cc38999e566eeed6a7",
#       @time=1521292325>

b3 = Block.new( 'Data Data Data Data', b2.hash )
#=> #<Block:0x4dbd9d0
#       @data="Data Data Data Data",
#       @difficulty="0000",
#       @nonce=43010,
#       @prev="00009b581870a4e0792f84786e1d089e32f2820459cd878298c6b62974afd0bc",
#       @time=1521292326>

Blockchain broken? Let's run all the tests checking up on the chained / linked (crypto) hashes, timestamps, proof-of-work difficulty and more:

## shortcut convenience helper
def sha256( data )
  Digest::SHA256.hexdigest( data )
end

b0.hash == sha256( "#{b0.nonce}#{b0.time}#{b0.difficulty}#{b0.prev}#{b0.data}" )
# => true
b1.hash == sha256( "#{b1.nonce}#{b1.time}#{b1.difficulty}#{b1.prev}#{b1.data}" )
# => true
b2.hash == sha256( "#{b2.nonce}#{b2.time}#{b2.difficulty}#{b2.prev}#{b2.data}" )
# => true
b3.hash == sha256( "#{b3.nonce}#{b3.time}#{b3.difficulty}#{b3.prev}#{b3.data}" )
# => true

# check proof-of-work difficulty (e.g. '0000')
b0.hash.start_with?( b0.difficulty )
# => true
b1.hash.start_with?( b1.difficulty )
# => true
b2.hash.start_with?( b2.difficulty )
# => true
b3.hash.start_with?( b3.difficulty )
# => true

## check chained / linked hashes
b0.prev == '0000000000000000000000000000000000000000000000000000000000000000'
#=> true
b1.prev == b0.hash
#=> true
b2.prev == b1.hash
#=> true
b3.prev == b2.hash
#=> true

# check time moving forward; timestamp always greater/bigger/younger
b1.time > b0.time
#=> true
b2.time > b1.time
#=> true
b3.time > b2.time
#=> true
Time.now.to_i > b3.time   ## back to the future (not yet) possible :-)
#=> true

All true, true, true, true, true, true, true, true. All in order? Yes. The blockchain is (almost) unbreakable.