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.



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

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



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

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)


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

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



126
127
128
129
130
131
132
133
134
# File 'lib/bitcoin.rb', line 126

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



217
218
219
# File 'lib/bitcoin.rb', line 217

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

#bitcoin_elliptic_curveObject



193
194
195
# File 'lib/bitcoin.rb', line 193

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

#bitcoin_hash(hex) ⇒ Object



211
212
213
214
215
# File 'lib/bitcoin.rb', line 211

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

#bitcoin_mrkl(a, b) ⇒ Object



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

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

#bitcoin_signed_message_hash(message) ⇒ Object



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

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)



411
412
413
# File 'lib/bitcoin.rb', line 411

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



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

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



435
436
437
# File 'lib/bitcoin.rb', line 435

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



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

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



223
224
225
226
227
# File 'lib/bitcoin.rb', line 223

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)



399
400
401
402
# File 'lib/bitcoin.rb', line 399

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



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

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.



353
354
355
# File 'lib/bitcoin.rb', line 353

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.



405
406
407
408
# File 'lib/bitcoin.rb', line 405

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



240
241
242
243
244
# File 'lib/bitcoin.rb', line 240

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.



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

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.



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

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



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

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



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/bitcoin.rb', line 151

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_target(target_bits) ⇒ Object



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

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



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

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

#encode_base58(hex) ⇒ Object



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

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)



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

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



206
207
208
209
# File 'lib/bitcoin.rb', line 206

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

#generate_keyObject



197
198
199
200
# File 'lib/bitcoin.rb', line 197

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.



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

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.



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

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

#hash160_to_address(hex) ⇒ Object



94
95
96
# File 'lib/bitcoin.rb', line 94

def hash160_to_address(hex)
  encode_address hex, address_version
end

#hash160_to_p2sh_address(hex) ⇒ Object



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

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



257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/bitcoin.rb', line 257

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.



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

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



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

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

#int_to_base58(int_val, leading_zero_bytes = 0) ⇒ Object



116
117
118
119
120
121
122
123
124
# File 'lib/bitcoin.rb', line 116

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



229
230
231
232
233
234
235
236
237
238
# File 'lib/bitcoin.rb', line 229

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.



272
273
274
275
276
277
278
279
# File 'lib/bitcoin.rb', line 272

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



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

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



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

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

#pubkey_to_address(pubkey) ⇒ Object



107
108
109
# File 'lib/bitcoin.rb', line 107

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

#pubkeys_to_p2sh_multisig_address(m, *pubkeys) ⇒ Object



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

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



322
323
324
# File 'lib/bitcoin.rb', line 322

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

#sha256(hex) ⇒ Object



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

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

#sign_data(key, data) ⇒ Object



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/bitcoin.rb', line 281

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



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

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)


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

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

#valid_pubkey?(pubkey) ⇒ Boolean

check if given pubkey is valid.

Returns:

  • (Boolean)


68
69
70
71
72
73
# File 'lib/bitcoin.rb', line 68

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



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

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



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

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