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.



114
115
116
117
118
119
120
# File 'lib/bitcoin.rb', line 114

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



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

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)


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

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



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

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



245
246
247
# File 'lib/bitcoin.rb', line 245

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

#bitcoin_elliptic_curveObject



217
218
219
220
221
222
223
# File 'lib/bitcoin.rb', line 217

def bitcoin_elliptic_curve
  # TODO: This can produce values which do not pass the low S-value check
  # from BIP62. Need to look at how best to solve that (here or perhaps
  # through OpenSSL) once Bitcoin Core implements that check in its own
  # signature generation.
  ::OpenSSL::PKey::EC.new("secp256k1")
end

#bitcoin_hash(hex) ⇒ Object



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

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

#bitcoin_mrkl(a, b) ⇒ Object



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

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

#bitcoin_signed_message_hash(message) ⇒ Object



333
334
335
336
337
# File 'lib/bitcoin.rb', line 333

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)



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

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



422
423
424
425
426
# File 'lib/bitcoin.rb', line 422

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



441
442
443
# File 'lib/bitcoin.rb', line 441

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



364
365
366
367
368
369
370
# File 'lib/bitcoin.rb', line 364

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



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

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)



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

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



379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/bitcoin.rb', line 379

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.



359
360
361
# File 'lib/bitcoin.rb', line 359

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.



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

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



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

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.



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

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.



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

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



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

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



183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/bitcoin.rb', line 183

def decode_compact_bits(bits)
  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
end

#decode_target(target_bits) ⇒ Object



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

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



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

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

#encode_base58(hex) ⇒ Object



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

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)



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

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



234
235
236
237
# File 'lib/bitcoin.rb', line 234

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

#generate_keyObject



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

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.



71
72
73
74
# File 'lib/bitcoin.rb', line 71

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.



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

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

#hash160_to_address(hex) ⇒ Object



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

def hash160_to_address(hex)
  encode_address hex, address_version
end

#hash160_to_p2sh_address(hex) ⇒ Object



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

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



285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/bitcoin.rb', line 285

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.



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

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



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

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

#int_to_base58(int_val, leading_zero_bytes = 0) ⇒ Object



148
149
150
151
152
153
154
155
156
# File 'lib/bitcoin.rb', line 148

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



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

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.



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

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



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

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



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

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

#pubkey_to_address(pubkey) ⇒ Object



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

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

#pubkeys_to_p2sh_multisig_address(m, *pubkeys) ⇒ Object



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

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



329
330
331
# File 'lib/bitcoin.rb', line 329

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

#sha256(hex) ⇒ Object



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

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

#sign_data(key, data) ⇒ Object



309
310
311
# File 'lib/bitcoin.rb', line 309

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

#sign_message(private_key_hex, public_key_hex, message) ⇒ Object



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

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)


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

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)


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

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



345
346
347
348
349
350
351
352
353
354
355
# File 'lib/bitcoin.rb', line 345

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 = OpenSSL_EC.recover_compact(hash, signature)
  pubkey_to_address(pubkey) == address if pubkey
rescue => ex
  p [ex.message, ex.backtrace]; false
end

#verify_signature(hash, signature, public_key) ⇒ Object



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

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