Class: Ciri::Chain::HeaderChain

Inherits:
Object
  • Object
show all
Defined in:
lib/ciri/chain/header_chain.rb

Constant Summary collapse

HEAD =
'head'
GENESIS =
'genesis'
HEADER_PREFIX =
'h'
TD_SUFFIX =
't'
NUM_SUFFIX =
'n'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(store, fork_config: nil) ⇒ HeaderChain

Returns a new instance of HeaderChain.



15
16
17
18
# File 'lib/ciri/chain/header_chain.rb', line 15

def initialize(store, fork_config: nil)
  @store = store
  @fork_config = fork_config
end

Instance Attribute Details

#storeObject (readonly)

Returns the value of attribute store.



13
14
15
# File 'lib/ciri/chain/header_chain.rb', line 13

def store
  @store
end

Instance Method Details

#calculate_difficulty(header, parent_header) ⇒ Object

calculate header difficulty you can find explain in Ethereum yellow paper: Block Header Validity section.



70
71
72
73
74
75
# File 'lib/ciri/chain/header_chain.rb', line 70

def calculate_difficulty(header, parent_header)
  return header.difficulty if header.number == 0

  fork_schema = @fork_config.choose_fork(header.number)
  fork_schema.calculate_difficulty(header, parent_header)
end

#get_header(hash) ⇒ Object



29
30
31
32
# File 'lib/ciri/chain/header_chain.rb', line 29

def get_header(hash)
  encoded = store[HEADER_PREFIX + hash]
  encoded && Header.rlp_decode(encoded)
end

#get_header_by_number(number) ⇒ Object



34
35
36
37
# File 'lib/ciri/chain/header_chain.rb', line 34

def get_header_by_number(number)
  hash = get_header_hash_by_number(number)
  hash && get_header(hash)
end

#get_header_hash_by_number(number) ⇒ Object



101
102
103
104
# File 'lib/ciri/chain/header_chain.rb', line 101

def get_header_hash_by_number(number)
  enc_number = Utils.big_endian_encode number
  store[HEADER_PREFIX + enc_number + NUM_SUFFIX]
end

#headObject



20
21
22
23
# File 'lib/ciri/chain/header_chain.rb', line 20

def head
  encoded = store[HEAD]
  encoded && Header.rlp_decode(encoded)
end

#head=(header) ⇒ Object



25
26
27
# File 'lib/ciri/chain/header_chain.rb', line 25

def head=(header)
  store[HEAD] = header.rlp_encode
end

#total_difficulty(header_hash = head.nil? ? nil : head.get_hash) ⇒ Object



106
107
108
109
# File 'lib/ciri/chain/header_chain.rb', line 106

def total_difficulty(header_hash = head.nil? ? nil : head.get_hash)
  return 0 if header_hash.nil?
  RLP.decode(store[HEADER_PREFIX + header_hash + TD_SUFFIX], Integer)
end

#valid?(header) ⇒ Boolean

Returns:

  • (Boolean)


39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/ciri/chain/header_chain.rb', line 39

def valid?(header)
  # ignore genesis header if there not exist one
  return true if header.number == 0 && get_header_by_number(0).nil?

  parent_header = get_header(header.parent_hash)
  return false unless parent_header
  # check height
  return false unless parent_header.number + 1 == header.number
  # check timestamp
  return false unless parent_header.timestamp < header.timestamp

  # check gas limit range
  parent_gas_limit = parent_header.gas_limit
  gas_limit_max = parent_gas_limit + parent_gas_limit / 1024
  gas_limit_min = parent_gas_limit - parent_gas_limit / 1024
  gas_limit = header.gas_limit
  return false unless gas_limit >= 5000 && gas_limit > gas_limit_min && gas_limit < gas_limit_max
  return false unless calculate_difficulty(header, parent_header) == header.difficulty

  # check pow
  begin
    POW.check_pow(header.number, header.mining_hash, header.mix_hash, header.nonce, header.difficulty)
  rescue POW::InvalidError
    return false
  end

  true
end

#write(header) ⇒ Object

write header



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/ciri/chain/header_chain.rb', line 78

def write(header)
  hash = header.get_hash
  # get total difficulty
  td = if header.number == 0
         header.difficulty
       else
         parent_header = get_header(header.parent_hash)
         raise "can't find parent from db" unless parent_header
         parent_td = total_difficulty(parent_header.get_hash)
         parent_td + header.difficulty
       end
  # write header and td
  store.batch do |b|
    b.put(HEADER_PREFIX + hash, header.rlp_encode)
    b.put(HEADER_PREFIX + hash + TD_SUFFIX, RLP.encode(td, Integer))
  end
end

#write_header_hash_number(header_hash, number) ⇒ Object



96
97
98
99
# File 'lib/ciri/chain/header_chain.rb', line 96

def write_header_hash_number(header_hash, number)
  enc_number = Utils.big_endian_encode number
  store[HEADER_PREFIX + enc_number + NUM_SUFFIX] = header_hash
end