Class: Blockchain

Inherits:
Object
  • Object
show all
Defined in:
lib/rubocoin/blockchain/blockchain.rb

Overview

This class defines what our blockchain will look like as well as contain functoins for interacting with the chain.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeBlockchain

Initialize the chain and other variables as well as create the ‘Genesis’ block



26
27
28
29
30
31
32
33
34
35
# File 'lib/rubocoin/blockchain/blockchain.rb', line 26

def initialize
  @current_transactions = []
  @chain = []
  @nodes = Set.new
  @difficulty = 4
  @difficulty_increment_counter = 0  # When this reaches 10000, increment difficulty by 1

  # Generate the genesis block
  new_block(100, 1)
end

Instance Attribute Details

#chainArray

The current chain being used



13
14
15
# File 'lib/rubocoin/blockchain/blockchain.rb', line 13

def chain
  @chain
end

#difficultyObject (readonly)

The difficulty of this node. It’s base value is 4 (meaning ‘0’*4) Each 10,000 blocks mined, the difficulty will increment by 1 (meaning ‘0’*5)



22
23
24
# File 'lib/rubocoin/blockchain/blockchain.rb', line 22

def difficulty
  @difficulty
end

#nodesSet

We use a Chain instead of an Array here because we don’t want any duplicate blocks in our chain. A Set takes care of this for us



18
19
20
# File 'lib/rubocoin/blockchain/blockchain.rb', line 18

def nodes
  @nodes
end

Instance Method Details

#calculate_reward_amountObject

The reward halfs each time the difficulty increases



38
39
40
41
42
43
44
# File 'lib/rubocoin/blockchain/blockchain.rb', line 38

def calculate_reward_amount
  initial_reward = 100
  (@difficulty - 3).times do
    initial_reward /= 2
  end
  return initial_reward
end

#hash(last_block) ⇒ Object

Hash the last block in the chain



195
196
197
198
# File 'lib/rubocoin/blockchain/blockchain.rb', line 195

def hash(last_block)
  block_string = last_block.sort.to_h.to_json
  return Digest::SHA256.hexdigest(block_string)
end

#last_blockObject

Return the most recent block in the chain



56
57
58
# File 'lib/rubocoin/blockchain/blockchain.rb', line 56

def last_block
  @chain[-1]
end

#new_block(proof, previous_hash = nil) ⇒ Object

Adds a new block to the blockchain

See Also:



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/rubocoin/blockchain/blockchain.rb', line 130

def new_block(proof, previous_hash=nil)
  # Define the block structure
  block =
    {
      :index => @chain.size + 1,
      :timestamp => Time.now,
      :transactions => @current_transactions,
      :proof => proof,
      :previous_hash => previous_hash || hash(last_block)
    }

    # Clear transactions
    @current_transactions = []
    info_with_timestamp('Transaction list cleared due to new block formation')

    # Append the block to the chain
    @chain << block

    print_new("New block added:")
    puts '{'
    puts "\t index: #{block[:index]}"
    puts "\t timestamp: #{block[:timestamp]}"
    puts "\t transactions: #{block[:transactions]}"
    puts "\t proof: #{block[:proof]}"
    puts "\t previous_hash: #{block[:previous_hash]}"
    puts '}'

    @difficulty_increment_counter += 1

    info("Difficulty will increase after #{1000-@difficulty_increment_counter} more blocks")

    if @difficulty_increment_counter >= 0x2710
      debug_with_timestamp("Difficulty increased to #{@difficulty + 1}!")
      @difficulty += 1
      @difficulty_increment_counter = 0 # Reset the counter
    end

    block
end

#new_transaction(sender, recipient, amount, others = nil) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/rubocoin/blockchain/blockchain.rb', line 170

def new_transaction(sender, recipient, amount, others=nil)
  unless others.nil?
    @current_transactions <<
      {
        :sender => sender,
        :recipient => recipient,
        :amount => amount,
        :others => others
      }
  else
    @current_transactions <<
      {
        :sender => sender,
        :recipient => recipient,
        :amount => amount
      }
  end

  last_block[:index] + 1
end

#proof_of_work(last_proof) ⇒ Object

Iterates through integers until it finds a valid value

See Also:



205
206
207
208
209
210
# File 'lib/rubocoin/blockchain/blockchain.rb', line 205

def proof_of_work(last_proof)
  proof = -1
  proof += 1 until valid_proof(last_proof, proof)
  info("Valid proof found! #{proof}")
  return proof
end

#register_node(addr) ⇒ Object

Register a new node



50
51
52
53
# File 'lib/rubocoin/blockchain/blockchain.rb', line 50

def register_node(addr)
  url = URI.parse(addr)
  @nodes.add("#{url.host}:#{url.port}")
end

#resolve_conflictsObject

Resolve conflicts with neighbour nodes. This gets the chain of each of the neighbour nodes and checks it with ours.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/rubocoin/blockchain/blockchain.rb', line 91

def resolve_conflicts
  neighbour_nodes = @nodes
  new_chain = nil

  # Only chains longer than ours
  max_len = @chain.size

  # Fetch and confirm each chain from the network
  neighbour_nodes.each do |node|
    response = JSON.parse(open("http://#{node}/chain").read)
    response.deep_symbolize_keys!

    if response[:chain]
      length = response[:length]
      chain = response[:chain]

      # Check if the length is longer and the chain is valid
      if length > max_len || valid_chain(chain)
        max_len = length
        new_chain = chain
      end
    end
  end

  # Replace our chain if we discovered a new, valid chain longer than ours
  if new_chain
    @chain = new_chain
    return true
  end

  return false
end

#valid_chain(chain) ⇒ Object

Check whether a chain is valid



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/rubocoin/blockchain/blockchain.rb', line 64

def valid_chain(chain)
  last_block = chain[0]
  index = 1

  while index < chain.size do
    block = chain[index]

    # Check previous block hash is correct
    if block[:previous_hash] != hash(last_block)
      false
    end

    # Check Proof of Work
    unless valid_proof(last_block[:proof], block[:proof])
      false
    end

    last_block = block
    index += 1
  end
  true  # If we got here, all blocks are valid; therefore the chain is valid
end

#valid_proof(last_proof, proof) ⇒ Object

Checks whether a value is a valid PoW



217
218
219
220
221
# File 'lib/rubocoin/blockchain/blockchain.rb', line 217

def valid_proof(last_proof, proof)
  guess = "#{last_proof}#{proof}"
  guess_hash = Digest::SHA256.hexdigest(guess)
  return guess_hash[0..(@difficulty - 1)] == ('0' * @difficulty)
end