Module: Bitcoin::Util

Included in:
Bitcoin
Defined in:
lib/bitcoin.rb

Constant Summary

RETARGET_INTERVAL =
2016

Instance Method Summary collapse

Instance Method Details

#address_type(address) ⇒ Object

get type of given address.



102
103
104
105
106
107
108
# File 'lib/bitcoin.rb', line 102

def address_type(address)
  return nil unless valid_address?(address)
  case decode_base58(address)[0...2]
  when address_version; :hash160
  when p2sh_version;    :p2sh
  end
end

#address_versionObject



63
# File 'lib/bitcoin.rb', line 63

def address_version; Bitcoin.network[:address_version]; end

#base58_checksum?(base58) ⇒ Boolean Also known as: address_checksum?

verify base58 checksum for given base58 data.



79
80
81
82
83
# File 'lib/bitcoin.rb', line 79

def base58_checksum?(base58)
  hex = decode_base58(base58) rescue nil
  return false unless hex
  Bitcoin.checksum( hex[0...42] ) == hex[-8..-1]
end

#base58_to_int(base58_val) ⇒ Object



141
142
143
144
145
146
147
148
149
# File 'lib/bitcoin.rb', line 141

def base58_to_int(base58_val)
  alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
  int_val, base = 0, alpha.size
  base58_val.reverse.each_char.with_index do |char,index|
    raise ArgumentError, 'Value not a valid Base58 String.' unless char_index = alpha.index(char)
    int_val += char_index*(base**index)
  end
  int_val
end

#bitcoin_byte_hash(bytes) ⇒ Object



219
220
221
# File 'lib/bitcoin.rb', line 219

def bitcoin_byte_hash(bytes)
  Digest::SHA256.digest(Digest::SHA256.digest(bytes))
end

#bitcoin_elliptic_curveObject



195
196
197
# File 'lib/bitcoin.rb', line 195

def bitcoin_elliptic_curve
  ::OpenSSL::PKey::EC.new("secp256k1")
end

#bitcoin_hash(hex) ⇒ Object



213
214
215
216
217
# File 'lib/bitcoin.rb', line 213

def bitcoin_hash(hex)
  Digest::SHA256.digest(
    Digest::SHA256.digest( [hex].pack("H*").reverse )
  ).reverse.unpack("H*")[0]
end

#bitcoin_mrkl(a, b) ⇒ Object



223
# File 'lib/bitcoin.rb', line 223

def bitcoin_mrkl(a, b); bitcoin_hash(b + a); end

#bitcoin_signed_message_hash(message) ⇒ Object



289
290
291
292
293
# File 'lib/bitcoin.rb', line 289

def bitcoin_signed_message_hash(message)
  # TODO: this will fail horribly on messages with len > 255. It's a cheap implementation of Bitcoin's CDataStream.
  data = "\x18Bitcoin Signed Message:\n" + [message.bytesize].pack("C") + message
  Digest::SHA256.digest(Digest::SHA256.digest(data))
end

#block_average_hashing_time(target_nbits, hashes_per_second) ⇒ Object

average time to find a block in seconds with the current target. (nbits)



343
344
345
# File 'lib/bitcoin.rb', line 343

def block_average_hashing_time(target_nbits, hashes_per_second)
  block_hashes_to_win(target_nbits) / hashes_per_second
end

#block_average_mining_time(block_nbits, block_height, mega_hashes_per_second, target_btc = 1.0) ⇒ Object

average mining time (in days) using Mh/s to get btc



348
349
350
351
352
# File 'lib/bitcoin.rb', line 348

def block_average_mining_time(block_nbits, block_height, mega_hashes_per_second, target_btc=1.0)
  seconds = block_average_hashing_time(block_nbits, mega_hashes_per_second * 1_000_000)
  reward  = block_creation_reward(block_height) / Bitcoin::COIN # satoshis to btc
  (days = seconds / 60 / 60 / 24) * (target_btc / reward)
end

#block_creation_reward(block_height) ⇒ Object



367
368
369
# File 'lib/bitcoin.rb', line 367

def block_creation_reward(block_height)
  5000000000 / (2 ** (block_height / 210000.0).floor)
end

#block_difficulty(target_nbits) ⇒ Object

current difficulty as a multiple of the minimum difficulty (highest target).



322
323
324
325
326
327
328
# File 'lib/bitcoin.rb', line 322

def block_difficulty(target_nbits)
  # max_target      = 0x00000000ffff0000000000000000000000000000000000000000000000000000
  # current_target  = Bitcoin.decode_compact_bits(target_nbits).to_i(16)
  # "%.7f" % (max_target / current_target.to_f)
  bits, max_body, scaland = target_nbits, Math.log(0x00ffff), Math.log(256)
  "%.7f" % Math.exp(max_body - Math.log(bits&0x00ffffff) + scaland * (0x1d - ((bits&0xff000000)>>24)))
end

#block_hash(prev_block, mrkl_root, time, bits, nonce, ver) ⇒ Object



225
226
227
228
229
# File 'lib/bitcoin.rb', line 225

def block_hash(prev_block, mrkl_root, time, bits, nonce, ver)
  h = "%08x%08x%08x%064s%064s%08x" %
        [nonce, bits, time, mrkl_root, prev_block, ver]
  bitcoin_hash(h)
end

#block_hashes_to_win(target_nbits) ⇒ Object

average number of hashes required to win a block with the current target. (nbits)



331
332
333
334
# File 'lib/bitcoin.rb', line 331

def block_hashes_to_win(target_nbits)
  current_target  = Bitcoin.decode_compact_bits(target_nbits).to_i(16)
  0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff / current_target
end

#block_next_retarget(block_height) ⇒ Object

block count when the next retarget will take place.



317
318
319
# File 'lib/bitcoin.rb', line 317

def block_next_retarget(block_height)
  (block_height + (RETARGET_INTERVAL-block_height.divmod(RETARGET_INTERVAL).last)) - 1
end

#block_probability(target_nbits) ⇒ Object

probability of a single hash solving a block with the current difficulty.



337
338
339
340
# File 'lib/bitcoin.rb', line 337

def block_probability(target_nbits)
  current_target  = Bitcoin.decode_compact_bits(target_nbits).to_i(16)
  "%.55f" % (current_target.to_f / 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
end

#blockchain_total_btc(height) ⇒ Object

shows the total number of Bitcoins in circulation, reward era and reward in that era.



355
356
357
358
359
360
361
362
363
364
365
# File 'lib/bitcoin.rb', line 355

def blockchain_total_btc(height)
  reward, interval = 5000000000, 210000
  total_btc = reward
  reward_era, remainder = (height).divmod(interval)
  reward_era.times{
    total_btc += interval * reward
    reward = reward / 2
  }
  total_btc += remainder * reward
  [total_btc, reward_era+1, reward, height]
end

#checksum(hex) ⇒ Object

checksum is a 4 bytes sha256-sha256 hexdigest.



73
74
75
76
# File 'lib/bitcoin.rb', line 73

def checksum(hex)
  b = [hex].pack("H*") # unpack hex
  Digest::SHA256.hexdigest( Digest::SHA256.digest(b) )[0...8]
end

#decode_base58(base58_val) ⇒ Object Also known as: base58_to_hex



157
158
159
160
161
162
163
# File 'lib/bitcoin.rb', line 157

def decode_base58(base58_val)
  s = base58_to_int(base58_val).to_s(16); s = (s.bytesize.odd? ? '0'+s : s)
  s = '' if s == '00'
  leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : '').size
  s = ("00"*leading_zero_bytes) + s  if leading_zero_bytes > 0
  s
end

#decode_compact_bits(bits) ⇒ Object

target compact bits (int) to bignum hex



167
168
169
170
171
172
173
# File 'lib/bitcoin.rb', line 167

def decode_compact_bits(bits)
  bytes = Array.new(size=((bits >> 24) & 255), 0)
  bytes[0] = (bits >> 16) & 255 if size >= 1
  bytes[1] = (bits >>  8) & 255 if size >= 2
  bytes[2] = (bits      ) & 255 if size >= 3
  bytes.pack("C*").unpack("H*")[0].rjust(64, '0')
end

#decode_target(target_bits) ⇒ Object



186
187
188
189
190
191
192
193
# File 'lib/bitcoin.rb', line 186

def decode_target(target_bits)
  case target_bits
  when Fixnum
    [ decode_compact_bits(target_bits).to_i(16), target_bits ]
  when String
    [ target_bits.to_i(16), encode_compact_bits(target_bits) ]
  end
end

#encode_address(hex, version) ⇒ Object



122
123
124
125
# File 'lib/bitcoin.rb', line 122

def encode_address(hex, version)
  hex = version + hex
  encode_base58(hex + checksum(hex))
end

#encode_base58(hex) ⇒ Object



151
152
153
154
# File 'lib/bitcoin.rb', line 151

def encode_base58(hex)
  leading_zero_bytes  = (hex.match(/^([0]+)/) ? $1 : '').size / 2
  ("1"*leading_zero_bytes) + int_to_base58( hex.to_i(16) )
end

#encode_compact_bits(target) ⇒ Object

target bignum hex to compact bits (int)



176
177
178
179
180
181
182
183
184
# File 'lib/bitcoin.rb', line 176

def encode_compact_bits(target)
  bytes = OpenSSL::BN.new(target, 16).to_mpi
  size = bytes.size - 4
  nbits = size << 24
  nbits |= (bytes[4] << 16) if size >= 1
  nbits |= (bytes[5] <<  8) if size >= 2
  nbits |= (bytes[6]      ) if size >= 3
  nbits
end

#generate_addressObject



208
209
210
211
# File 'lib/bitcoin.rb', line 208

def generate_address
  prvkey, pubkey = generate_key
  [ pubkey_to_address(pubkey), prvkey, pubkey, hash160(pubkey) ]
end

#generate_keyObject



199
200
201
202
# File 'lib/bitcoin.rb', line 199

def generate_key
  key = bitcoin_elliptic_curve.generate_key
  inspect_key( key )
end

#hash160(hex) ⇒ Object

hash160 is a 20 bytes (160bits) rmd610-sha256 hexdigest.



67
68
69
70
# File 'lib/bitcoin.rb', line 67

def hash160(hex)
  bytes = [hex].pack("H*")
  Digest::RMD160.hexdigest Digest::SHA256.digest(bytes)
end

#hash160_from_address(address) ⇒ Object

get hash160 for given address. returns nil if address is invalid.



96
97
98
99
# File 'lib/bitcoin.rb', line 96

def hash160_from_address(address)
  return nil  unless valid_address?(address)
  decode_base58(address)[2...42]
end

#hash160_to_address(hex) ⇒ Object



114
115
116
# File 'lib/bitcoin.rb', line 114

def hash160_to_address(hex)
  encode_address hex, address_version
end

#hash160_to_p2sh_address(hex) ⇒ Object



118
119
120
# File 'lib/bitcoin.rb', line 118

def hash160_to_p2sh_address(hex)
  encode_address hex, p2sh_version
end

#hash_mrkl_branch(tx, target) ⇒ Object

get merkle branch connecting given target to the merkle root of tx list



243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/bitcoin.rb', line 243

def hash_mrkl_branch(tx, target)
  return [ nil ]  if tx != tx.uniq
  branch, chunks = [], [ tx.dup ]
  while chunks.last.size >= 2
    chunks << chunks.last.each_slice(2).map {|a, b|
      hash = Bitcoin.bitcoin_mrkl( a, b || a )
      next hash  unless [a, b].include?(target)
      branch << (a == target ? (b || a) : a)
      target = hash
    }
  end
  branch
end

#hash_mrkl_tree(tx) ⇒ Object

get merkle tree for given tx list.



232
233
234
235
236
237
238
239
240
# File 'lib/bitcoin.rb', line 232

def hash_mrkl_tree(tx)
  return [nil]  if tx != tx.uniq
  chunks = [ tx.dup ]
  while chunks.last.size >= 2
    chunks << chunks.last.each_slice(2).map {|a, b|
      Bitcoin.bitcoin_mrkl( a, b || a ) }
  end
  chunks.flatten
end

#inspect_key(key) ⇒ Object



204
205
206
# File 'lib/bitcoin.rb', line 204

def inspect_key(key)
  [ key.private_key_hex, key.public_key_hex ]
end

#int_to_base58(int_val, leading_zero_bytes = 0) ⇒ Object



131
132
133
134
135
136
137
138
139
# File 'lib/bitcoin.rb', line 131

def int_to_base58(int_val, leading_zero_bytes=0)
  alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
  base58_val, base = '', alpha.size
  while int_val > 0
    int_val, remainder = int_val.divmod(base)
    base58_val = alpha[remainder] + base58_val
  end
  base58_val
end

#mrkl_branch_root(branch, target, idx) ⇒ Object

get merkle root from branch and target.



258
259
260
261
262
263
# File 'lib/bitcoin.rb', line 258

def mrkl_branch_root(branch, target, idx)
  branch.map do |hash|
    a, b = *( idx & 1 == 0 ? [target, hash] : [hash, target] )
    idx >>= 1; target = Bitcoin.bitcoin_mrkl( a, b )
  end.last
end

#open_key(private_key, public_key = nil) ⇒ Object



277
278
279
280
281
282
283
# File 'lib/bitcoin.rb', line 277

def open_key(private_key, public_key=nil)
  key  = bitcoin_elliptic_curve
  key.private_key = ::OpenSSL::BN.from_hex(private_key)
  public_key = regenerate_public_key(private_key) unless public_key
  key.public_key  = ::OpenSSL::PKey::EC::Point.from_hex(key.group, public_key)
  key
end

#p2sh_versionObject



64
# File 'lib/bitcoin.rb', line 64

def p2sh_version; Bitcoin.network[:p2sh_version]; end

#pubkey_to_address(pubkey) ⇒ Object



127
128
129
# File 'lib/bitcoin.rb', line 127

def pubkey_to_address(pubkey)
  hash160_to_address( hash160(pubkey) )
end

#regenerate_public_key(private_key) ⇒ Object



285
286
287
# File 'lib/bitcoin.rb', line 285

def regenerate_public_key(private_key)
  Bitcoin::OpenSSL_EC.regenerate_key(private_key)[1]
end

#sha256(hex) ⇒ Object



110
111
112
# File 'lib/bitcoin.rb', line 110

def sha256(hex)
  Digest::SHA256.hexdigest([hex].pack("H*"))
end

#sign_data(key, data) ⇒ Object



265
266
267
# File 'lib/bitcoin.rb', line 265

def sign_data(key, data)
  key.dsa_sign_asn1(data)
end

#sign_message(private_key_hex, public_key_hex, message) ⇒ Object



295
296
297
298
299
# File 'lib/bitcoin.rb', line 295

def sign_message(private_key_hex, public_key_hex, message)
  hash = bitcoin_signed_message_hash(message)
  signature = Bitcoin::OpenSSL_EC.sign_compact(hash, private_key_hex, public_key_hex)
  { 'address' => pubkey_to_address(public_key_hex), 'message' => message, 'signature' => [ signature ].pack("m0") }
end

#valid_address?(address) ⇒ Boolean

check if given address is valid. this means having a correct version byte, length and checksum.



88
89
90
91
92
93
# File 'lib/bitcoin.rb', line 88

def valid_address?(address)
  hex = decode_base58(address) rescue nil
  return false unless hex && hex.bytesize == 50
  return false unless [address_version, p2sh_version].include?(hex[0...2])
  address_checksum?(address)
end

#verify_message(address, signature, message) ⇒ Object



301
302
303
304
305
306
307
308
309
310
311
# File 'lib/bitcoin.rb', line 301

def verify_message(address, signature, message)
  hash = bitcoin_signed_message_hash(message)
  signature = signature.unpack("m0")[0] rescue nil # decode base64
  raise "invalid address"           unless valid_address?(address)
  raise "malformed base64 encoding" unless signature
  raise "malformed signature"       unless signature.bytesize == 65
  pubkey = Bitcoin::OpenSSL_EC.recover_compact(hash, signature)
  pubkey_to_address(pubkey) == address if pubkey
rescue Exception => ex
  p [ex.message, ex.backtrace]; false
end

#verify_signature(hash, signature, public_key) ⇒ Object



269
270
271
272
273
274
275
# File 'lib/bitcoin.rb', line 269

def verify_signature(hash, signature, public_key)
  key  = bitcoin_elliptic_curve
  key.public_key = ::OpenSSL::PKey::EC::Point.from_hex(key.group, public_key)
  key.dsa_verify_asn1(hash, signature)
rescue OpenSSL::PKey::ECError, OpenSSL::PKey::EC::Point::Error
  false
end