Module: Bitcoin::Util
- Included in:
- Bitcoin
- Defined in:
- lib/bitcoin.rb
Instance Method Summary collapse
-
#address_type(address) ⇒ Object
get type of given
address
. - #address_version ⇒ Object
-
#base58_checksum?(base58) ⇒ Boolean
(also: #address_checksum?)
verify base58 checksum for given
base58
data. - #base58_to_int(base58_val) ⇒ Object
- #bitcoin_byte_hash(bytes) ⇒ Object
- #bitcoin_elliptic_curve ⇒ Object
- #bitcoin_hash(hex) ⇒ Object
- #bitcoin_mrkl(a, b) ⇒ Object
- #bitcoin_signed_message_hash(message) ⇒ Object
-
#block_average_hashing_time(target_nbits, hashes_per_second) ⇒ Object
average time to find a block in seconds with the current target.
-
#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.
- #block_creation_reward(block_height) ⇒ Object
-
#block_difficulty(target_nbits) ⇒ Object
current difficulty as a multiple of the minimum difficulty (highest target).
- #block_hash(prev_block, mrkl_root, time, bits, nonce, ver) ⇒ Object
-
#block_hashes_to_win(target_nbits) ⇒ Object
average number of hashes required to win a block with the current target.
-
#block_new_target(prev_height, prev_block_time, prev_block_bits, last_retarget_time) ⇒ Object
Calculate new difficulty target.
-
#block_next_retarget(block_height) ⇒ Object
block count when the next retarget will take place.
-
#block_probability(target_nbits) ⇒ Object
probability of a single hash solving a block with the current difficulty.
- #block_scrypt_hash(prev_block, mrkl_root, time, bits, nonce, ver) ⇒ Object
-
#blockchain_total_btc(height) ⇒ Object
shows the total number of Bitcoins in circulation, reward era and reward in that era.
-
#checksum(hex) ⇒ Object
checksum is a 4 bytes sha256-sha256 hexdigest.
- #decode_base58(base58_val) ⇒ Object (also: #base58_to_hex)
-
#decode_compact_bits(bits) ⇒ Object
target compact bits (int) to bignum hex.
- #decode_target(target_bits) ⇒ Object
- #encode_address(hex, version) ⇒ Object
- #encode_base58(hex) ⇒ Object
-
#encode_compact_bits(target) ⇒ Object
target bignum hex to compact bits (int).
- #generate_address ⇒ Object
- #generate_key ⇒ Object
-
#hash160(hex) ⇒ Object
hash160 is a 20 bytes (160bits) rmd610-sha256 hexdigest.
-
#hash160_from_address(address) ⇒ Object
get hash160 for given
address
. - #hash160_to_address(hex) ⇒ Object
- #hash160_to_p2sh_address(hex) ⇒ Object
-
#hash_mrkl_branch(tx, target) ⇒ Object
get merkle branch connecting given
target
to the merkle root oftx
list. -
#hash_mrkl_tree(tx) ⇒ Object
get merkle tree for given
tx
list. - #inspect_key(key) ⇒ Object
- #int_to_base58(int_val, leading_zero_bytes = 0) ⇒ Object
- #litecoin_hash(hex) ⇒ Object
-
#mrkl_branch_root(branch, target, idx) ⇒ Object
get merkle root from
branch
andtarget
. - #open_key(private_key, public_key = nil) ⇒ Object
- #p2sh_version ⇒ Object
- #pubkey_to_address(pubkey) ⇒ Object
- #pubkeys_to_p2sh_multisig_address(m, *pubkeys) ⇒ Object
- #regenerate_public_key(private_key) ⇒ Object
- #sha256(hex) ⇒ Object
- #sign_data(key, data) ⇒ Object
- #sign_message(private_key_hex, public_key_hex, message) ⇒ Object
-
#valid_address?(address) ⇒ Boolean
check if given
address
is valid. -
#valid_pubkey?(pubkey) ⇒ Boolean
check if given
pubkey
is valid. - #verify_message(address, signature, message) ⇒ Object
- #verify_signature(hash, signature, public_key) ⇒ Object
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_version ⇒ Object
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.
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_curve ⇒ Object
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 () = .dup.force_encoding('binary') magic = Bitcoin.network[:message_magic] buf = Protocol.pack_var_int(magic.bytesize) + magic buf << Protocol.pack_var_int(.bytesize) + 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_address ⇒ Object
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_key ⇒ Object
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_version ⇒ Object
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 (private_key_hex, public_key_hex, ) hash = () signature = OpenSSL_EC.sign_compact(hash, private_key_hex, public_key_hex) { 'address' => pubkey_to_address(public_key_hex), '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.
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.
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 (address, signature, ) 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 = () 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 |