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
# 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)
    case hex[0...2]
    when address_version
      return :hash160
    when p2sh_version
      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



192
193
194
195
196
197
198
199
200
# File 'lib/bitcoin.rb', line 192

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



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

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

#bitcoin_elliptic_curveObject



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

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

#bitcoin_hash(hex) ⇒ Object



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

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

#bitcoin_mrkl(a, b) ⇒ Object



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

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

#bitcoin_signed_message_hash(message) ⇒ Object



392
393
394
395
396
397
398
399
400
# File 'lib/bitcoin.rb', line 392

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)



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

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



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

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



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

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



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

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



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

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)



465
466
467
468
# File 'lib/bitcoin.rb', line 465

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



439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/bitcoin.rb', line 439

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.



419
420
421
# File 'lib/bitcoin.rb', line 419

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.



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

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



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

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.



489
490
491
492
493
494
495
496
497
498
499
# File 'lib/bitcoin.rb', line 489

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



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

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



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/bitcoin.rb', line 217

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



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/bitcoin.rb', line 158

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



250
251
252
253
254
255
256
257
# File 'lib/bitcoin.rb', line 250

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



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

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

#encode_base58(hex) ⇒ Object



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

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)



240
241
242
243
244
245
246
247
248
# File 'lib/bitcoin.rb', line 240

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



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/bitcoin.rb', line 140

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



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

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

#generate_keyObject



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

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



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

def hash160_to_address(hex)
  encode_address hex, address_version
end

#hash160_to_p2sh_address(hex) ⇒ Object



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

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



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

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.



313
314
315
316
317
318
319
320
# File 'lib/bitcoin.rb', line 313

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



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

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

#int_to_base58(int_val, leading_zero_bytes = 0) ⇒ Object



182
183
184
185
186
187
188
189
190
# File 'lib/bitcoin.rb', line 182

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



295
296
297
298
299
300
301
302
303
304
# File 'lib/bitcoin.rb', line 295

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.



338
339
340
341
342
343
344
345
# File 'lib/bitcoin.rb', line 338

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



380
381
382
383
384
385
386
# File 'lib/bitcoin.rb', line 380

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



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

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

#pubkeys_to_p2sh_multisig_address(m, *pubkeys) ⇒ Object



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

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



388
389
390
# File 'lib/bitcoin.rb', line 388

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

#sha256(hex) ⇒ Object



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

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

#sign_data(key, data) ⇒ Object



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/bitcoin.rb', line 347

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



402
403
404
405
406
# File 'lib/bitcoin.rb', line 402

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



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

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



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

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