bitcoin-ruby 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. data/.gitignore +12 -0
  2. data/COPYING +18 -0
  3. data/Gemfile +4 -0
  4. data/README.rdoc +189 -0
  5. data/Rakefile +104 -0
  6. data/bin/bitcoin_dns_seed +130 -0
  7. data/bin/bitcoin_gui +80 -0
  8. data/bin/bitcoin_node +174 -0
  9. data/bin/bitcoin_shell +12 -0
  10. data/bin/bitcoin_wallet +323 -0
  11. data/bitcoin-ruby.gemspec +27 -0
  12. data/concept-examples/blockchain-pow.rb +151 -0
  13. data/doc/CONFIG.rdoc +66 -0
  14. data/doc/EXAMPLES.rdoc +9 -0
  15. data/doc/NODE.rdoc +35 -0
  16. data/doc/STORAGE.rdoc +21 -0
  17. data/doc/WALLET.rdoc +102 -0
  18. data/examples/balance.rb +60 -0
  19. data/examples/bbe_verify_tx.rb +55 -0
  20. data/examples/connect.rb +36 -0
  21. data/examples/relay_tx.rb +22 -0
  22. data/examples/verify_tx.rb +57 -0
  23. data/lib/bitcoin.rb +370 -0
  24. data/lib/bitcoin/builder.rb +266 -0
  25. data/lib/bitcoin/config.rb +56 -0
  26. data/lib/bitcoin/connection.rb +126 -0
  27. data/lib/bitcoin/ffi/openssl.rb +121 -0
  28. data/lib/bitcoin/gui/addr_view.rb +42 -0
  29. data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
  30. data/lib/bitcoin/gui/bitcoin-ruby.svg +80 -0
  31. data/lib/bitcoin/gui/conn_view.rb +36 -0
  32. data/lib/bitcoin/gui/connection.rb +68 -0
  33. data/lib/bitcoin/gui/em_gtk.rb +28 -0
  34. data/lib/bitcoin/gui/gui.builder +1643 -0
  35. data/lib/bitcoin/gui/gui.rb +290 -0
  36. data/lib/bitcoin/gui/helpers.rb +113 -0
  37. data/lib/bitcoin/gui/tree_view.rb +82 -0
  38. data/lib/bitcoin/gui/tx_view.rb +67 -0
  39. data/lib/bitcoin/key.rb +125 -0
  40. data/lib/bitcoin/logger.rb +65 -0
  41. data/lib/bitcoin/network/command_client.rb +93 -0
  42. data/lib/bitcoin/network/command_handler.rb +179 -0
  43. data/lib/bitcoin/network/connection_handler.rb +274 -0
  44. data/lib/bitcoin/network/node.rb +399 -0
  45. data/lib/bitcoin/protocol.rb +140 -0
  46. data/lib/bitcoin/protocol/address.rb +48 -0
  47. data/lib/bitcoin/protocol/alert.rb +47 -0
  48. data/lib/bitcoin/protocol/block.rb +154 -0
  49. data/lib/bitcoin/protocol/handler.rb +38 -0
  50. data/lib/bitcoin/protocol/parser.rb +148 -0
  51. data/lib/bitcoin/protocol/tx.rb +205 -0
  52. data/lib/bitcoin/protocol/txin.rb +97 -0
  53. data/lib/bitcoin/protocol/txout.rb +73 -0
  54. data/lib/bitcoin/protocol/version.rb +70 -0
  55. data/lib/bitcoin/script.rb +634 -0
  56. data/lib/bitcoin/storage/dummy.rb +164 -0
  57. data/lib/bitcoin/storage/models.rb +133 -0
  58. data/lib/bitcoin/storage/sequel.rb +335 -0
  59. data/lib/bitcoin/storage/sequel_store/sequel_migrations.rb +84 -0
  60. data/lib/bitcoin/storage/storage.rb +243 -0
  61. data/lib/bitcoin/version.rb +3 -0
  62. data/lib/bitcoin/wallet/coinselector.rb +30 -0
  63. data/lib/bitcoin/wallet/keygenerator.rb +75 -0
  64. data/lib/bitcoin/wallet/keystore.rb +203 -0
  65. data/lib/bitcoin/wallet/txdp.rb +116 -0
  66. data/lib/bitcoin/wallet/wallet.rb +243 -0
  67. data/spec/bitcoin/bitcoin_spec.rb +472 -0
  68. data/spec/bitcoin/builder_spec.rb +90 -0
  69. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +27 -0
  70. data/spec/bitcoin/fixtures/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +23 -0
  71. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +27 -0
  72. data/spec/bitcoin/fixtures/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +45 -0
  73. data/spec/bitcoin/fixtures/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +34 -0
  74. data/spec/bitcoin/fixtures/rawblock-0.bin +0 -0
  75. data/spec/bitcoin/fixtures/rawblock-0.json +39 -0
  76. data/spec/bitcoin/fixtures/rawblock-1.bin +0 -0
  77. data/spec/bitcoin/fixtures/rawblock-1.json +39 -0
  78. data/spec/bitcoin/fixtures/rawblock-131025.bin +0 -0
  79. data/spec/bitcoin/fixtures/rawblock-131025.json +5063 -0
  80. data/spec/bitcoin/fixtures/rawblock-170.bin +0 -0
  81. data/spec/bitcoin/fixtures/rawblock-170.json +68 -0
  82. data/spec/bitcoin/fixtures/rawblock-9.bin +0 -0
  83. data/spec/bitcoin/fixtures/rawblock-9.json +39 -0
  84. data/spec/bitcoin/fixtures/rawblock-testnet-26478.bin +0 -0
  85. data/spec/bitcoin/fixtures/rawblock-testnet-26478.json +64 -0
  86. data/spec/bitcoin/fixtures/rawtx-01.bin +0 -0
  87. data/spec/bitcoin/fixtures/rawtx-01.json +27 -0
  88. data/spec/bitcoin/fixtures/rawtx-02.bin +0 -0
  89. data/spec/bitcoin/fixtures/rawtx-02.json +27 -0
  90. data/spec/bitcoin/fixtures/rawtx-03.bin +0 -0
  91. data/spec/bitcoin/fixtures/rawtx-03.json +48 -0
  92. data/spec/bitcoin/fixtures/rawtx-04.json +27 -0
  93. data/spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
  94. data/spec/bitcoin/fixtures/rawtx-05.json +23 -0
  95. data/spec/bitcoin/fixtures/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
  96. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
  97. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +27 -0
  98. data/spec/bitcoin/fixtures/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +23 -0
  99. data/spec/bitcoin/fixtures/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
  100. data/spec/bitcoin/fixtures/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
  101. data/spec/bitcoin/fixtures/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +37 -0
  102. data/spec/bitcoin/fixtures/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +24 -0
  103. data/spec/bitcoin/fixtures/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +23 -0
  104. data/spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
  105. data/spec/bitcoin/fixtures/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +27 -0
  106. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
  107. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +41 -0
  108. data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
  109. data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
  110. data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
  111. data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
  112. data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
  113. data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
  114. data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
  115. data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
  116. data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
  117. data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
  118. data/spec/bitcoin/fixtures/txdp-1.txt +32 -0
  119. data/spec/bitcoin/fixtures/txdp-2-signed.txt +19 -0
  120. data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +14 -0
  121. data/spec/bitcoin/key_spec.rb +123 -0
  122. data/spec/bitcoin/network_spec.rb +48 -0
  123. data/spec/bitcoin/protocol/addr_spec.rb +68 -0
  124. data/spec/bitcoin/protocol/alert_spec.rb +20 -0
  125. data/spec/bitcoin/protocol/block_spec.rb +101 -0
  126. data/spec/bitcoin/protocol/inv_spec.rb +124 -0
  127. data/spec/bitcoin/protocol/ping_spec.rb +49 -0
  128. data/spec/bitcoin/protocol/tx_spec.rb +226 -0
  129. data/spec/bitcoin/protocol/version_spec.rb +77 -0
  130. data/spec/bitcoin/reorg_spec.rb +129 -0
  131. data/spec/bitcoin/script/opcodes_spec.rb +417 -0
  132. data/spec/bitcoin/script/script_spec.rb +246 -0
  133. data/spec/bitcoin/spec_helper.rb +36 -0
  134. data/spec/bitcoin/storage_spec.rb +229 -0
  135. data/spec/bitcoin/wallet/coinselector_spec.rb +35 -0
  136. data/spec/bitcoin/wallet/keygenerator_spec.rb +64 -0
  137. data/spec/bitcoin/wallet/keystore_spec.rb +188 -0
  138. data/spec/bitcoin/wallet/txdp_spec.rb +74 -0
  139. data/spec/bitcoin/wallet/wallet_spec.rb +207 -0
  140. metadata +295 -0
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Fetch a transaction from blockexplorer.com and verify all signatures.
4
+ #
5
+ # examples/bbe_verify_tx.rb <tx hash> [testnet]
6
+ # examples/bbe_verify_tx.rb f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16
7
+ # see Bitcoin::P::Tx and Bitcoin::Script.
8
+
9
+ $:.unshift(File.dirname(__FILE__) + "/../lib")
10
+ require 'bitcoin'
11
+ require 'open-uri'
12
+
13
+ tx_hash = ARGV[0]
14
+ $testnet = ARGV[1]
15
+
16
+ # fetch transaction from bbe as json and deserialize into Bitcoin::Protocol::Tx object
17
+ def get_tx(hash)
18
+ url = "http://blockexplorer.com/%srawtx/%s" % [$testnet ? 'testnet/' : '', hash]
19
+ json = open(url).read
20
+ Bitcoin::Protocol::Tx.from_json(json)
21
+ rescue Exception
22
+ nil
23
+ end
24
+
25
+ tx1 = get_tx(tx_hash)
26
+
27
+ unless tx1
28
+ puts "Tx #{tx_hash} not found."
29
+ exit
30
+ end
31
+
32
+ if tx1.in.all?{|txin| txin.coinbase? }
33
+ puts "Tx #{tx_hash} is a coinbase transaction. Check the block instead."
34
+ exit
35
+ end
36
+
37
+ tx1.in.each_with_index do |txin, idx|
38
+ if txin.coinbase?
39
+ puts "skipping coinbase transaction input.."; next
40
+ end
41
+
42
+ prev_tx = get_tx(txin.previous_output)
43
+ unless prev_tx
44
+ puts "Missing prev_out tx for input #{idx} of tx #{tx_hash}!"
45
+ exit
46
+ end
47
+
48
+ result = tx1.verify_input_signature(idx, prev_tx)
49
+ unless result
50
+ puts "Input #{idx} of tx #{tx_hash} is invalid!"
51
+ exit
52
+ end
53
+ end
54
+
55
+ puts "Tx #{tx_hash} is valid."
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Connect to a random peer and download the first 500 blocks.
4
+ #
5
+ # examples/connect.rb [testnet]
6
+ #
7
+ # see Bitcoin::Connection and Bitcoin::Protocol.
8
+
9
+ $:.unshift( File.expand_path("../../lib", __FILE__) )
10
+ require 'bitcoin/connection'
11
+
12
+ Bitcoin::network = ARGV[0] || :bitcoin
13
+
14
+ class RawJSON_Connection < Bitcoin::Connection
15
+ def on_tx(tx)
16
+ p ['tx', tx.hash, Time.now]
17
+ # puts tx.to_json
18
+ end
19
+
20
+ def on_block(block)
21
+ p ['block', block.hash, Time.now]
22
+ # puts block.to_json
23
+ end
24
+ end
25
+
26
+ EM.run do
27
+
28
+ host = '127.0.0.1'
29
+ #host = '217.157.1.202'
30
+
31
+ connections = []
32
+ #RawJSON_Connection.connect(host, 8333, connections)
33
+
34
+ RawJSON_Connection.connect_random_from_dns(connections)
35
+
36
+ end
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Relay transaction to the network.
4
+ # TODO
5
+
6
+ require 'socket'
7
+ require 'json'
8
+
9
+ # TODO: use CommandClient
10
+
11
+ host, port = "127.0.0.1", 9999
12
+ if ARGV[0] == "-s"
13
+ host, port = ARGV[1].split(":")
14
+ ARGV.shift; ARGV.shift
15
+ end
16
+
17
+ s = TCPSocket.new("127.0.0.1", 9999)
18
+ s.puts ("relay_tx " + ARGF.read.unpack("H*")[0])
19
+
20
+ res = s.readline
21
+ puts JSON::pretty_generate(JSON::parse(res))
22
+ s.close
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Fetch a transaction and all its previous outputs from local storage and verify signatures.
4
+ #
5
+ # examples/verify_tx.rb <tx_hash>
6
+ # examples/verify_tx.rb f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16
7
+ #
8
+ # see Bitcoin::Protocol::Tx and Bitcoin::Script.
9
+ # Note: For this to work, you need to have the transactions in your storage. see NODE.
10
+
11
+
12
+ $:.unshift( File.expand_path("../../lib", __FILE__) )
13
+ require 'bitcoin'
14
+
15
+ Bitcoin.network = :bitcoin
16
+ store = Bitcoin::Storage.sequel(:db => "sqlite://bitcoin.db")
17
+
18
+ tx_hash = ARGV.shift
19
+
20
+ tx1 = store.get_tx(tx_hash)
21
+
22
+ unless tx1
23
+ puts "Tx #{tx_hash} not found."
24
+ exit
25
+ end
26
+
27
+ if tx1.in.all?{|txin| txin.coinbase? }
28
+ puts "Tx #{tx_hash} is a coinbase transaction. Check the block instead."
29
+ exit
30
+ end
31
+
32
+ tx1.in.each_with_index do |txin, idx|
33
+ if txin.coinbase?
34
+ puts "skipping coinbase transaction input.."; next
35
+ end
36
+
37
+ prev_tx = txin.get_prev_out.get_tx
38
+ if prev_tx
39
+ puts "Found prev tx #{prev_tx.hash}"
40
+ txout = prev_tx.out[txin.prev_out_index]
41
+ script = Bitcoin::Script.new(txout.pk_script)
42
+ puts "Output Script: #{script.to_string}"
43
+ else
44
+ puts "Missing prev tx for input #{idx}!"
45
+ exit
46
+ end
47
+
48
+ result = tx1.verify_input_signature(idx, prev_tx)
49
+ if result
50
+ puts "Valid signature for input #{idx}."
51
+ else
52
+ puts "Signature for input #{idx} is invalid!"
53
+ exit
54
+ end
55
+ end
56
+
57
+ puts "Tx #{tx_hash} is valid."
data/lib/bitcoin.rb ADDED
@@ -0,0 +1,370 @@
1
+ # Bitcoin Utils and Network Protocol in Ruby.
2
+
3
+ require 'digest/sha2'
4
+ require 'digest/rmd160'
5
+ require 'openssl'
6
+
7
+
8
+ module Bitcoin
9
+
10
+ autoload :Connection, 'bitcoin/connection'
11
+ autoload :Protocol, 'bitcoin/protocol'
12
+ autoload :P, 'bitcoin/protocol'
13
+ autoload :Script, 'bitcoin/script'
14
+ autoload :VERSION, 'bitcoin/version'
15
+ autoload :Storage, 'bitcoin/storage/storage'
16
+ autoload :Logger, 'bitcoin/logger'
17
+ autoload :Key, 'bitcoin/key'
18
+ autoload :Config, 'bitcoin/config'
19
+ autoload :Builder, 'bitcoin/builder'
20
+
21
+ module Network
22
+ autoload :ConnectionHandler, 'bitcoin/network/connection_handler'
23
+ autoload :CommandHandler, 'bitcoin/network/command_handler'
24
+ autoload :CommandClient, 'bitcoin/network/command_client'
25
+ autoload :Node, 'bitcoin/network/node'
26
+ end
27
+
28
+ module Wallet
29
+ autoload :KeyGenerator, 'bitcoin/wallet/keygenerator'
30
+ autoload :SimpleKeyStore, 'bitcoin/wallet/keystore'
31
+ autoload :DeterministicKeyStore, 'bitcoin/wallet/keystore'
32
+ autoload :SimpleCoinSelector, 'bitcoin/wallet/coinselector'
33
+ autoload :Wallet, 'bitcoin/wallet/wallet'
34
+ autoload :TxDP, 'bitcoin/wallet/txdp'
35
+ end
36
+
37
+ module Gui
38
+ autoload :Gui, 'bitcoin/gui/gui'
39
+ autoload :Connection, 'bitcoin/gui/connection'
40
+ end
41
+
42
+ def self.require_dependency name, opts = {}
43
+ begin
44
+ require name.to_s
45
+ rescue LoadError
46
+ print "Cannot load #{opts[:exit] == false ? 'optional' : 'required'} dependency '#{name}'"
47
+ (opts[:gem] == false) ? puts("") :
48
+ puts(" - install with `gem install #{opts[:gem] || name}`")
49
+ puts opts[:message] if opts[:message]
50
+ exit 1 unless opts[:exit] == false
51
+ return false
52
+ end
53
+ true
54
+ end
55
+
56
+ module Util
57
+
58
+ def hth(h); h.unpack("H*")[0]; end
59
+ def htb(h); [h].pack("H*"); end
60
+
61
+ def address_version; Bitcoin.network[:address_version]; end
62
+ def p2sh_version; Bitcoin.network[:p2sh_version]; end
63
+
64
+ # hash160 is a 20 bytes (160bits) rmd610-sha256 hexdigest.
65
+ def hash160(hex)
66
+ bytes = [hex].pack("H*")
67
+ Digest::RMD160.hexdigest Digest::SHA256.digest(bytes)
68
+ end
69
+
70
+ # checksum is a 4 bytes sha256-sha256 hexdigest.
71
+ def checksum(hex)
72
+ b = [hex].pack("H*") # unpack hex
73
+ Digest::SHA256.hexdigest( Digest::SHA256.digest(b) )[0...8]
74
+ end
75
+
76
+ # verify base58 checksum for given +base58+ data.
77
+ def base58_checksum?(base58)
78
+ hex = decode_base58(base58) rescue nil
79
+ return false unless hex
80
+ Bitcoin.checksum( hex[0...42] ) == hex[-8..-1]
81
+ end
82
+ alias :address_checksum? :base58_checksum?
83
+
84
+ # check if given +address+ is valid.
85
+ # this means having a correct version byte, length and checksum.
86
+ def valid_address?(address)
87
+ hex = decode_base58(address) rescue nil
88
+ return false unless hex && hex.bytesize == 50
89
+ return false unless [address_version, p2sh_version].include?(hex[0...2])
90
+ address_checksum?(address)
91
+ end
92
+
93
+ # get hash160 for given +address+. returns nil if address is invalid.
94
+ def hash160_from_address(address)
95
+ return nil unless valid_address?(address)
96
+ decode_base58(address)[2...42]
97
+ end
98
+
99
+ # get type of given +address+.
100
+ def address_type(address)
101
+ return nil unless valid_address?(address)
102
+ case decode_base58(address)[0...2]
103
+ when address_version; :hash160
104
+ when p2sh_version; :p2sh
105
+ end
106
+ end
107
+
108
+ def sha256(hex)
109
+ Digest::SHA256.hexdigest([hex].pack("H*"))
110
+ end
111
+
112
+ def hash160_to_address(hex)
113
+ hex = address_version + hex
114
+ encode_base58(hex + checksum(hex))
115
+ end
116
+
117
+ def pubkey_to_address(pubkey)
118
+ hash160_to_address( hash160(pubkey) )
119
+ end
120
+
121
+ def int_to_base58(int_val, leading_zero_bytes=0)
122
+ alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
123
+ base58_val, base = '', alpha.size
124
+ while int_val > 0
125
+ int_val, remainder = int_val.divmod(base)
126
+ base58_val = alpha[remainder] + base58_val
127
+ end
128
+ base58_val
129
+ end
130
+
131
+ def base58_to_int(base58_val)
132
+ alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
133
+ int_val, base = 0, alpha.size
134
+ base58_val.reverse.each_char.with_index do |char,index|
135
+ raise ArgumentError, 'Value not a valid Base58 String.' unless char_index = alpha.index(char)
136
+ int_val += char_index*(base**index)
137
+ end
138
+ int_val
139
+ end
140
+
141
+ def encode_base58(hex)
142
+ leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : '').size / 2
143
+ ("1"*leading_zero_bytes) + int_to_base58( hex.to_i(16) )
144
+ end
145
+
146
+
147
+ def decode_base58(base58_val)
148
+ s = base58_to_int(base58_val).to_s(16); s = (s.bytesize.odd? ? '0'+s : s)
149
+ s = '' if s == '00'
150
+ leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : '').size
151
+ s = ("00"*leading_zero_bytes) + s if leading_zero_bytes > 0
152
+ s
153
+ end
154
+ alias_method :base58_to_hex, :decode_base58
155
+
156
+ # target compact bits (int) to bignum hex
157
+ def decode_compact_bits(bits)
158
+ bytes = Array.new(size=((bits >> 24) & 255), 0)
159
+ bytes[0] = (bits >> 16) & 255 if size >= 1
160
+ bytes[1] = (bits >> 8) & 255 if size >= 2
161
+ bytes[2] = (bits ) & 255 if size >= 3
162
+ bytes.pack("C*").unpack("H*")[0].rjust(64, '0')
163
+ end
164
+
165
+ # target bignum hex to compact bits (int)
166
+ def encode_compact_bits(target)
167
+ bytes = OpenSSL::BN.new(target, 16).to_mpi
168
+ size = bytes.size - 4
169
+ nbits = size << 24
170
+ nbits |= (bytes[4] << 16) if size >= 1
171
+ nbits |= (bytes[5] << 8) if size >= 2
172
+ nbits |= (bytes[6] ) if size >= 3
173
+ nbits
174
+ end
175
+
176
+ def decode_target(target_bits)
177
+ case target_bits
178
+ when Fixnum
179
+ [ decode_compact_bits(target_bits).to_i(16), target_bits ]
180
+ when String
181
+ [ target_bits.to_i(16), encode_compact_bits(target_bits) ]
182
+ end
183
+ end
184
+
185
+ def bitcoin_elliptic_curve
186
+ ::OpenSSL::PKey::EC.new("secp256k1")
187
+ end
188
+
189
+ def generate_key
190
+ key = bitcoin_elliptic_curve.generate_key
191
+ inspect_key( key )
192
+ end
193
+
194
+ def inspect_key(key)
195
+ [ key.private_key_hex, key.public_key_hex ]
196
+ end
197
+
198
+ def generate_address
199
+ prvkey, pubkey = generate_key
200
+ [ pubkey_to_address(pubkey), prvkey, pubkey, hash160(pubkey) ]
201
+ end
202
+
203
+ def bitcoin_hash(hex)
204
+ Digest::SHA256.digest(
205
+ Digest::SHA256.digest( [hex].pack("H*").reverse )
206
+ ).reverse.unpack("H*")[0]
207
+ end
208
+
209
+ def bitcoin_mrkl(a, b); bitcoin_hash(b + a); end
210
+
211
+ def block_hash(prev_block, mrkl_root, time, bits, nonce, ver)
212
+ h = "%08x%08x%08x%064s%064s%08x" %
213
+ [nonce, bits, time, mrkl_root, prev_block, ver]
214
+ bitcoin_hash(h)
215
+ end
216
+
217
+ def hash_mrkl_tree(tx)
218
+ chunks = [ tx.dup ]
219
+ while chunks.last.size >= 2
220
+ chunks << chunks.last.each_slice(2).map{|i|
221
+ Bitcoin.bitcoin_mrkl( i[0], i[1] || i[0] )
222
+ }
223
+ end
224
+ chunks.flatten
225
+ end
226
+
227
+
228
+ def sign_data(key, data)
229
+ key.dsa_sign_asn1(data)
230
+ end
231
+
232
+ def verify_signature(hash, signature, public_key)
233
+ key = bitcoin_elliptic_curve
234
+ key.public_key = ::OpenSSL::PKey::EC::Point.from_hex(key.group, public_key)
235
+ key.dsa_verify_asn1(hash, signature)
236
+ rescue OpenSSL::PKey::ECError, OpenSSL::PKey::EC::Point::Error
237
+ false
238
+ end
239
+
240
+ def open_key(private_key, public_key=nil)
241
+ key = bitcoin_elliptic_curve
242
+ key.private_key = ::OpenSSL::BN.from_hex(private_key)
243
+ public_key = regenerate_public_key(private_key) unless public_key
244
+ key.public_key = ::OpenSSL::PKey::EC::Point.from_hex(key.group, public_key)
245
+ key
246
+ end
247
+
248
+ def regenerate_public_key(private_key)
249
+ Bitcoin::OpenSSL_EC.regenerate_key(private_key)[1]
250
+ end
251
+
252
+
253
+ RETARGET_INTERVAL = 2016
254
+
255
+ # block count when the next retarget will take place.
256
+ def block_next_retarget(block_height)
257
+ (block_height + (RETARGET_INTERVAL-block_height.divmod(RETARGET_INTERVAL).last)) - 1
258
+ end
259
+
260
+ # current difficulty as a multiple of the minimum difficulty (highest target).
261
+ def block_difficulty(target_nbits)
262
+ # max_target = 0x00000000ffff0000000000000000000000000000000000000000000000000000
263
+ # current_target = Bitcoin.decode_compact_bits(target_nbits).to_i(16)
264
+ # "%.7f" % (max_target / current_target.to_f)
265
+ bits, max_body, scaland = target_nbits, Math.log(0x00ffff), Math.log(256)
266
+ "%.7f" % Math.exp(max_body - Math.log(bits&0x00ffffff) + scaland * (0x1d - ((bits&0xff000000)>>24)))
267
+ end
268
+
269
+ # average number of hashes required to win a block with the current target. (nbits)
270
+ def block_hashes_to_win(target_nbits)
271
+ current_target = Bitcoin.decode_compact_bits(target_nbits).to_i(16)
272
+ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff / current_target
273
+ end
274
+
275
+ # probability of a single hash solving a block with the current difficulty.
276
+ def block_probability(target_nbits)
277
+ current_target = Bitcoin.decode_compact_bits(target_nbits).to_i(16)
278
+ "%.55f" % (current_target.to_f / 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
279
+ end
280
+
281
+ # average time to find a block in seconds with the current target. (nbits)
282
+ def block_average_hashing_time(target_nbits, hashes_per_second)
283
+ block_hashes_to_win(target_nbits) / hashes_per_second
284
+ end
285
+
286
+ # shows the total number of Bitcoins in circulation, reward era and reward in that era.
287
+ def blockchain_total_btc(height)
288
+ reward, interval = 5000000000, 210000
289
+ total_btc = reward
290
+ reward_era, remainder = (height).divmod(interval)
291
+ reward_era.times{
292
+ total_btc += interval * reward
293
+ reward = reward / 2
294
+ }
295
+ total_btc += remainder * reward
296
+ [total_btc, reward_era+1, reward, height]
297
+ end
298
+
299
+ def block_creation_reward(block_height)
300
+ 5000000000 / (2 ** (block_height / 210000.0).floor)
301
+ end
302
+ end
303
+
304
+ module ::OpenSSL
305
+ class BN
306
+ def self.from_hex(hex); new(hex, 16); end
307
+ def to_hex; to_i.to_s(16); end
308
+ def to_mpi; to_s(0).unpack("C*"); end
309
+ end
310
+ class PKey::EC
311
+ def private_key_hex; private_key.to_hex.rjust(64, '0'); end
312
+ def public_key_hex; public_key.to_hex.rjust(130, '0'); end
313
+ end
314
+ class PKey::EC::Point
315
+ def self.from_hex(group, hex)
316
+ new(group, BN.from_hex(hex))
317
+ end
318
+ def to_hex; to_bn.to_hex; end
319
+ def self.bn2mpi(hex) BN.from_hex(hex).to_mpi; end
320
+ end
321
+ end
322
+
323
+ autoload :OpenSSL_EC, "bitcoin/ffi/openssl"
324
+
325
+
326
+ extend Util
327
+
328
+ @network = :bitcoin
329
+
330
+ def self.network
331
+ NETWORKS[@network]
332
+ end
333
+
334
+ def self.network= name
335
+ @network = name.to_sym
336
+ end
337
+
338
+ NETWORKS = {
339
+ :bitcoin => {
340
+ :magic_head => "\xF9\xBE\xB4\xD9",
341
+ :address_version => "00",
342
+ :p2sh_version => "05",
343
+ :privkey_version => "80",
344
+ :default_port => 8333,
345
+ :dns_seeds => ["bitseed.xf2.org", "dnsseed.bluematt.me",
346
+ "dnsseed.bitcoin.dashjr.org", "seed.bitcoin.sipa.be"],
347
+ :genesis_hash => "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
348
+ :proof_of_work_limit => 0x1d00ffff,
349
+ :known_nodes => [
350
+ 'relay.eligius.st',
351
+ 'mining.bitcoin.cz',
352
+ 'bitcoins.lc',
353
+ 'blockchain.info',
354
+ 'blockexplorer.com',
355
+ ]
356
+ },
357
+ :testnet => {
358
+ :magic_head => "\xFA\xBF\xB5\xDA",
359
+ :address_version => "6f",
360
+ :p2sh_version => "c4",
361
+ :privkey_version => "ef",
362
+ :default_port => 18333,
363
+ :dns_seeds => ["testseed.bitcoin.interesthings.de"],
364
+ :genesis_hash => "00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008",
365
+ :proof_of_work_limit => 0x1d07fff8,
366
+ :known_nodes => []
367
+ }
368
+ }
369
+
370
+ end