Module: Bitcoin::Util

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

Instance Method Summary collapse

Instance Method Details

#address_type(address) ⇒ Object

get type of given address.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/bitcoin.rb', line 106

def address_type(address)
  segwit_decoded = decode_segwit_address(address) rescue nil
  if segwit_decoded
    witness_version, witness_program_hex = segwit_decoded
    witness_program = [witness_program_hex].pack("H*")

    if witness_version == 0 && witness_program.bytesize == 20
      return :witness_v0_keyhash
    end

    if witness_version == 0 && witness_program.bytesize == 32
      return :witness_v0_scripthash
    end
  end

  hex = decode_base58(address) rescue nil

  target_size = (version_bytes + 20 + 4) * 2 # version_bytes + 20 bytes hash + 4 bytes checksum
  if hex && hex.bytesize == target_size && address_checksum?(address)
    # Litecoin updates the P2SH version byte, and this method should recognize both.
    p2sh_versions = [p2sh_version]
    if Bitcoin.network[:legacy_p2sh_versions]
      p2sh_versions += Bitcoin.network[:legacy_p2sh_versions]
    end

    case hex[0...(version_bytes * 2)]
    when address_version
      return :hash160
    when *p2sh_versions
      return :p2sh
    end
  end

  nil
end

#address_versionObject



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

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

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

verify base58 checksum for given base58 data.

Returns:

  • (Boolean)


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

def base58_checksum?(base58)
  hex = decode_base58(base58) rescue nil
  return false unless hex
  checksum(hex[0...(version_bytes + 20) * 2]) == hex[-8..-1]
end

#base58_to_int(base58_val) ⇒ Object



221
222
223
224
225
226
227
228
229
# File 'lib/bitcoin.rb', line 221

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



312
313
314
# File 'lib/bitcoin.rb', line 312

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

#bitcoin_elliptic_curveObject



288
289
290
# File 'lib/bitcoin.rb', line 288

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

#bitcoin_hash(hex) ⇒ Object



306
307
308
309
310
# File 'lib/bitcoin.rb', line 306

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

#bitcoin_mrkl(a, b) ⇒ Object



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

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

#bitcoin_signed_message_hash(message) ⇒ Object



421
422
423
424
425
426
427
428
429
# File 'lib/bitcoin.rb', line 421

def bitcoin_signed_message_hash(message)
  message = message.dup.force_encoding('binary')

  magic = Bitcoin.network[:message_magic]
  buf = Protocol.pack_var_int(magic.bytesize) + magic
  buf << Protocol.pack_var_int(message.bytesize) + message

  Digest::SHA256.digest(Digest::SHA256.digest(buf))
end

#block_average_hashing_time(target_nbits, hashes_per_second) ⇒ Object

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



506
507
508
# File 'lib/bitcoin.rb', line 506

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



511
512
513
514
515
# File 'lib/bitcoin.rb', line 511

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) / COIN # satoshis to btc
  (days = seconds / 60 / 60 / 24) * (target_btc / reward)
end

#block_creation_reward(block_height) ⇒ Object



530
531
532
# File 'lib/bitcoin.rb', line 530

def block_creation_reward(block_height)
  Bitcoin.network[:reward_base] / (2 ** (block_height / Bitcoin.network[:reward_halving].to_f).floor)
end

#block_difficulty(target_nbits) ⇒ Object

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



453
454
455
456
457
458
459
# File 'lib/bitcoin.rb', line 453

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



318
319
320
321
322
# File 'lib/bitcoin.rb', line 318

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)



494
495
496
497
# File 'lib/bitcoin.rb', line 494

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

#block_new_target(prev_height, prev_block_time, prev_block_bits, last_retarget_time) ⇒ Object

Calculate new difficulty target. Note this takes in details of the preceeding block, not the current one.

prev_height is the height of the block before the retarget occurs prev_block_time “time” field from the block before the retarget occurs prev_block_bits “bits” field from the block before the retarget occurs (target as a compact value) last_retarget_time is the “time” field from the block when a retarget last occurred



468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
# File 'lib/bitcoin.rb', line 468

def block_new_target(prev_height, prev_block_time, prev_block_bits, last_retarget_time)
  # target interval - what is the ideal interval between the blocks
  retarget_time = Bitcoin.network[:retarget_time]

  actual_time = prev_block_time - last_retarget_time

  min = retarget_time / 4
  max = retarget_time * 4

  actual_time = min if actual_time < min
  actual_time = max if actual_time > max

  # It could be a bit confusing: we are adjusting difficulty of the previous block, while logically
  # we should use difficulty of the previous retarget block

  prev_target = decode_compact_bits(prev_block_bits).to_i(16)

  new_target = prev_target * actual_time / retarget_time
  if new_target < Bitcoin.decode_compact_bits(Bitcoin.network[:proof_of_work_limit]).to_i(16)
    encode_compact_bits(new_target.to_s(16))
  else
    Bitcoin.network[:proof_of_work_limit]
  end
end

#block_next_retarget(block_height) ⇒ Object

block count when the next retarget will take place.



448
449
450
# File 'lib/bitcoin.rb', line 448

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.



500
501
502
503
# File 'lib/bitcoin.rb', line 500

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

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



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

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

#blockchain_total_btc(height) ⇒ Object

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



518
519
520
521
522
523
524
525
526
527
528
# File 'lib/bitcoin.rb', line 518

def blockchain_total_btc(height)
  reward, interval = Bitcoin.network[:reward_base], Bitcoin.network[:reward_halving]
  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.



60
61
62
63
# File 'lib/bitcoin.rb', line 60

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



236
237
238
239
240
241
242
# File 'lib/bitcoin.rb', line 236

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



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/bitcoin.rb', line 246

def decode_compact_bits(bits)
  if Bitcoin.network_project == :dogecoin
    bytes = Array.new(size=((bits >> 24) & 255), 0)
    bytes[0] = (bits >> 16) & 0x7f if size >= 1
    bytes[1] = (bits >>  8) & 255 if size >= 2
    bytes[2] = (bits      ) & 255 if size >= 3
    target = bytes.pack("C*").unpack("H*")[0].rjust(64, '0')
    # Bit number 24 represents the sign
    if (bits & 0x00800000) != 0
      "-" + target
    else
      target
    end
  else
    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
end

#decode_segwit_address(address) ⇒ Object



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/bitcoin.rb', line 187

def decode_segwit_address(address)
  hrp = Bitcoin.network[:bech32_hrp]
  return nil if hrp.nil?

  actual_hrp, data  = Bitcoin::Bech32.decode(address)

  return nil if actual_hrp.nil?
  length = data.size
  return nil if length == 0 || length > 65
  return nil if hrp != actual_hrp
  return nil if data[0] > 16


  program = Bitcoin::Bech32.convert_bits(data[1..-1], from_bits: 5, to_bits: 8, pad: false)
  return nil if program.nil?

  length = program.size
  return nil if length < 2 || length > 40
  return nil if data[0] == 0 && length != 20 && length != 32

  program_hex = program.pack("C*").unpack("H*").first
  return [data[0], program_hex]
end

#decode_target(target_bits) ⇒ Object



279
280
281
282
283
284
285
286
# File 'lib/bitcoin.rb', line 279

def decode_target(target_bits)
  case target_bits
  when Bitcoin::Integer
    [ 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



154
155
156
157
# File 'lib/bitcoin.rb', line 154

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

#encode_base58(hex) ⇒ Object



231
232
233
234
# File 'lib/bitcoin.rb', line 231

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)



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

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

#encode_segwit_address(version, program_hex) ⇒ Object



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/bitcoin.rb', line 169

def encode_segwit_address(version, program_hex)
  hrp = Bitcoin.network[:bech32_hrp]
  raise "Invalid network" if hrp.nil?

  program = [program_hex].pack("H*")

  return nil if version > 16
  length = program.size
  return nil if version == 0 && length != 20 && length != 32
  return nil if length < 2 || length > 40

  data = [ version ] + Bitcoin::Bech32.convert_bits(program.unpack("C*"), from_bits: 8, to_bits: 5, pad: true)

  address = Bitcoin::Bech32.encode(hrp, data)

  return address.nil? ? nil : address
end

#generate_addressObject



301
302
303
304
# File 'lib/bitcoin.rb', line 301

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

#generate_keyObject



292
293
294
295
# File 'lib/bitcoin.rb', line 292

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.



54
55
56
57
# File 'lib/bitcoin.rb', line 54

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.



93
94
95
96
97
98
99
100
101
102
103
# File 'lib/bitcoin.rb', line 93

def hash160_from_address(address)
  case address_type(address)
  when :witness_v0_keyhash
    _, witness_program_hex = decode_segwit_address(address)
    witness_program_hex
  when :hash160, :p2sh
    start_idx = version_bytes * 2
    stop_idx = start_idx + 40 # 20 bytes (2 chars per byte)
    decode_base58(address)[start_idx...stop_idx]
  end
end

#hash160_to_address(hex) ⇒ Object



146
147
148
# File 'lib/bitcoin.rb', line 146

def hash160_to_address(hex)
  encode_address hex, address_version
end

#hash160_to_p2sh_address(hex) ⇒ Object



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

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



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

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_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.



342
343
344
345
346
347
348
349
# File 'lib/bitcoin.rb', line 342

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_mrkl( a, b || a ) }
  end
  chunks.flatten
end

#inspect_key(key) ⇒ Object



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

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

#int_to_base58(int_val, leading_zero_bytes = 0) ⇒ Object



211
212
213
214
215
216
217
218
219
# File 'lib/bitcoin.rb', line 211

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

#litecoin_hash(hex) ⇒ Object



324
325
326
327
328
329
330
331
332
333
# File 'lib/bitcoin.rb', line 324

def litecoin_hash(hex)
  bytes = [hex].pack("H*").reverse
  begin
    require "scrypt" unless defined?(::SCrypt)
    hash = SCrypt::Engine.__sc_crypt(bytes, bytes, 1024, 1, 1, 32)
  rescue LoadError
    hash = Litecoin::Scrypt.scrypt_1024_1_1_256_sp(bytes)
  end
  hash.reverse.unpack("H*")[0]
end

#mrkl_branch_root(branch, target, idx) ⇒ Object

get merkle root from branch and target.



367
368
369
370
371
372
373
374
# File 'lib/bitcoin.rb', line 367

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

#open_key(private_key, public_key = nil) ⇒ Object



409
410
411
412
413
414
415
# File 'lib/bitcoin.rb', line 409

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



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

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

#pubkey_to_address(pubkey) ⇒ Object



159
160
161
# File 'lib/bitcoin.rb', line 159

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

#pubkeys_to_p2sh_multisig_address(m, *pubkeys) ⇒ Object



163
164
165
166
# File 'lib/bitcoin.rb', line 163

def pubkeys_to_p2sh_multisig_address(m, *pubkeys)
  redeem_script = Bitcoin::Script.to_p2sh_multisig_script(m, *pubkeys).last
  return Bitcoin.hash160_to_p2sh_address(Bitcoin.hash160(redeem_script.hth)), redeem_script
end

#regenerate_public_key(private_key) ⇒ Object



417
418
419
# File 'lib/bitcoin.rb', line 417

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

#sha256(hex) ⇒ Object



142
143
144
# File 'lib/bitcoin.rb', line 142

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

#sign_data(key, data) ⇒ Object



376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/bitcoin.rb', line 376

def sign_data(key, data)
  sig = nil
  loop {
    sig = key.dsa_sign_asn1(data)
    sig = if Script.is_low_der_signature?(sig)
            sig
          else
            Bitcoin::OpenSSL_EC.signature_to_low_s(sig)
          end

    buf = sig + [Script::SIGHASH_TYPE[:all]].pack("C") # is_der_signature expects sig + sighash_type format
    if Script.is_der_signature?(buf)
      break
    else
      p ["Bitcoin#sign_data: invalid der signature generated, trying again.", data.unpack("H*")[0], sig.unpack("H*")[0]]
    end
  }
  return sig
end

#sign_message(private_key_hex, public_key_hex, message) ⇒ Object



431
432
433
434
435
# File 'lib/bitcoin.rb', line 431

def sign_message(private_key_hex, public_key_hex, message)
  hash = bitcoin_signed_message_hash(message)
  signature = 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.

Returns:

  • (Boolean)


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

def valid_address?(address)
  address_type(address) != nil
end

#valid_pubkey?(pubkey) ⇒ Boolean

check if given pubkey is valid.

Returns:

  • (Boolean)


80
81
82
83
84
85
86
87
88
89
90
# File 'lib/bitcoin.rb', line 80

def valid_pubkey?(pubkey)
  ::OpenSSL::PKey::EC::Point.from_hex(bitcoin_elliptic_curve.group, pubkey)
  true
rescue OpenSSL::PKey::EC::Point::Error
  false
rescue OpenSSL::BNError
  # Occasionally, a malformed value will fail hex decoding completely and
  # instead of raising an `OpenSSL::PKey::EC::Point::Error` will raise this
  # error. We capture this failure mode here as well.
  false
end

#verify_message(address, signature, message) ⇒ Object



437
438
439
440
441
442
443
444
445
# File 'lib/bitcoin.rb', line 437

def verify_message(address, signature, message)
  signature = signature.unpack("m0")[0] rescue nil # decode base64
  return false unless valid_address?(address)
  return false unless signature
  return false unless signature.bytesize == 65
  hash = bitcoin_signed_message_hash(message)
  pubkey = OpenSSL_EC.recover_compact(hash, signature)
  pubkey_to_address(pubkey) == address if pubkey
end

#verify_signature(hash, signature, public_key) ⇒ Object



396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/bitcoin.rb', line 396

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

#version_bytesObject



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

def version_bytes; address_version.size / 2; end