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.



85
86
87
88
89
90
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
# File 'lib/bitcoin.rb', line 85

def address_type(address)
  segwit_decoded = decode_segwit_address(address)
  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
  if hex && hex.bytesize == 50 && 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...2]
    when address_version
      return :hash160
    when *p2sh_versions
      return :p2sh
    end
  end

  nil
end

#address_versionObject



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

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)


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

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

#base58_to_int(base58_val) ⇒ Object



198
199
200
201
202
203
204
205
206
# File 'lib/bitcoin.rb', line 198

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



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

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

#bitcoin_elliptic_curveObject



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

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

#bitcoin_hash(hex) ⇒ Object



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

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

#bitcoin_mrkl(a, b) ⇒ Object



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

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

#bitcoin_signed_message_hash(message) ⇒ Object



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

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)



483
484
485
# File 'lib/bitcoin.rb', line 483

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



488
489
490
491
492
# File 'lib/bitcoin.rb', line 488

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



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

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



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

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



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

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)



471
472
473
474
# File 'lib/bitcoin.rb', line 471

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



445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
# File 'lib/bitcoin.rb', line 445

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.



425
426
427
# File 'lib/bitcoin.rb', line 425

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.



477
478
479
480
# File 'lib/bitcoin.rb', line 477

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



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

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.



495
496
497
498
499
500
501
502
503
504
505
# File 'lib/bitcoin.rb', line 495

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.



46
47
48
49
# File 'lib/bitcoin.rb', line 46

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



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

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



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/bitcoin.rb', line 223

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



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

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



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

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



131
132
133
134
# File 'lib/bitcoin.rb', line 131

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

#encode_base58(hex) ⇒ Object



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

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)



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

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



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/bitcoin.rb', line 146

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



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

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

#generate_keyObject



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

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.



40
41
42
43
# File 'lib/bitcoin.rb', line 40

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.



74
75
76
77
78
79
80
81
82
# File 'lib/bitcoin.rb', line 74

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
    decode_base58(address)[2...42]
  end
end

#hash160_to_address(hex) ⇒ Object



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

def hash160_to_address(hex)
  encode_address hex, address_version
end

#hash160_to_p2sh_address(hex) ⇒ Object



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

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



329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/bitcoin.rb', line 329

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.



319
320
321
322
323
324
325
326
# File 'lib/bitcoin.rb', line 319

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



274
275
276
# File 'lib/bitcoin.rb', line 274

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

#int_to_base58(int_val, leading_zero_bytes = 0) ⇒ Object



188
189
190
191
192
193
194
195
196
# File 'lib/bitcoin.rb', line 188

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



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

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.



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

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



386
387
388
389
390
391
392
# File 'lib/bitcoin.rb', line 386

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



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

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

#pubkey_to_address(pubkey) ⇒ Object



136
137
138
# File 'lib/bitcoin.rb', line 136

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

#pubkeys_to_p2sh_multisig_address(m, *pubkeys) ⇒ Object



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

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



394
395
396
# File 'lib/bitcoin.rb', line 394

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

#sha256(hex) ⇒ Object



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

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

#sign_data(key, data) ⇒ Object



353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/bitcoin.rb', line 353

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



408
409
410
411
412
# File 'lib/bitcoin.rb', line 408

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)


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

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

#valid_pubkey?(pubkey) ⇒ Boolean

check if given pubkey is valid.

Returns:

  • (Boolean)


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

def valid_pubkey?(pubkey)
  ::OpenSSL::PKey::EC::Point.from_hex(bitcoin_elliptic_curve.group, pubkey)
  true
rescue OpenSSL::PKey::EC::Point::Error
  false
end

#verify_message(address, signature, message) ⇒ Object



414
415
416
417
418
419
420
421
422
# File 'lib/bitcoin.rb', line 414

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



373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/bitcoin.rb', line 373

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