bitcoin-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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