bitcoin-ruby 0.0.1 → 0.0.2

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 (136) hide show
  1. data/.gitignore +4 -1
  2. data/Gemfile +21 -0
  3. data/README.rdoc +85 -25
  4. data/Rakefile +7 -3
  5. data/bin/bitcoin_node +39 -42
  6. data/bin/bitcoin_shell +1 -0
  7. data/bin/bitcoin_wallet +129 -53
  8. data/bitcoin-ruby.gemspec +4 -7
  9. data/concept-examples/blockchain-pow.rb +1 -1
  10. data/doc/CONFIG.rdoc +5 -5
  11. data/doc/EXAMPLES.rdoc +9 -5
  12. data/doc/NAMECOIN.rdoc +34 -0
  13. data/doc/NODE.rdoc +147 -10
  14. data/examples/balance.rb +10 -4
  15. data/examples/bbe_verify_tx.rb +7 -2
  16. data/examples/forwarder.rb +73 -0
  17. data/examples/generate_tx.rb +34 -0
  18. data/examples/simple_network_monitor_and_util.rb +187 -0
  19. data/examples/verify_tx.rb +1 -1
  20. data/lib/bitcoin.rb +308 -18
  21. data/lib/bitcoin/builder.rb +62 -36
  22. data/lib/bitcoin/config.rb +2 -0
  23. data/lib/bitcoin/connection.rb +11 -8
  24. data/lib/bitcoin/electrum/mnemonic.rb +162 -0
  25. data/lib/bitcoin/ffi/openssl.rb +187 -21
  26. data/lib/bitcoin/gui/addr_view.rb +2 -0
  27. data/lib/bitcoin/gui/conn_view.rb +2 -0
  28. data/lib/bitcoin/gui/connection.rb +2 -0
  29. data/lib/bitcoin/gui/em_gtk.rb +2 -0
  30. data/lib/bitcoin/gui/gui.rb +2 -0
  31. data/lib/bitcoin/gui/helpers.rb +2 -0
  32. data/lib/bitcoin/gui/tree_view.rb +2 -0
  33. data/lib/bitcoin/gui/tx_view.rb +2 -0
  34. data/lib/bitcoin/key.rb +77 -11
  35. data/lib/bitcoin/litecoin.rb +81 -0
  36. data/lib/bitcoin/logger.rb +20 -1
  37. data/lib/bitcoin/namecoin.rb +279 -0
  38. data/lib/bitcoin/network/command_client.rb +7 -6
  39. data/lib/bitcoin/network/command_handler.rb +229 -43
  40. data/lib/bitcoin/network/connection_handler.rb +182 -70
  41. data/lib/bitcoin/network/node.rb +231 -106
  42. data/lib/bitcoin/protocol.rb +44 -23
  43. data/lib/bitcoin/protocol/address.rb +5 -3
  44. data/lib/bitcoin/protocol/alert.rb +3 -4
  45. data/lib/bitcoin/protocol/aux_pow.rb +123 -0
  46. data/lib/bitcoin/protocol/block.rb +98 -18
  47. data/lib/bitcoin/protocol/handler.rb +6 -5
  48. data/lib/bitcoin/protocol/parser.rb +44 -19
  49. data/lib/bitcoin/protocol/tx.rb +105 -52
  50. data/lib/bitcoin/protocol/txin.rb +39 -19
  51. data/lib/bitcoin/protocol/txout.rb +28 -13
  52. data/lib/bitcoin/protocol/version.rb +16 -7
  53. data/lib/bitcoin/script.rb +579 -122
  54. data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
  55. data/lib/bitcoin/storage/models.rb +20 -7
  56. data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
  57. data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
  58. data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
  59. data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
  60. data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
  61. data/lib/bitcoin/storage/storage.rb +233 -28
  62. data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
  63. data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
  64. data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
  65. data/lib/bitcoin/validation.rb +369 -0
  66. data/lib/bitcoin/version.rb +1 -1
  67. data/lib/bitcoin/wallet/coinselector.rb +3 -0
  68. data/lib/bitcoin/wallet/keygenerator.rb +3 -1
  69. data/lib/bitcoin/wallet/keystore.rb +6 -2
  70. data/lib/bitcoin/wallet/txdp.rb +6 -4
  71. data/lib/bitcoin/wallet/wallet.rb +54 -16
  72. data/spec/bitcoin/bitcoin_spec.rb +48 -3
  73. data/spec/bitcoin/builder_spec.rb +40 -17
  74. data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
  75. data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
  76. data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
  77. data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
  78. data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
  79. data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
  80. data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
  81. data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
  82. data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
  83. data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
  84. data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
  85. data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
  86. data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
  87. data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
  88. data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
  89. data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
  90. data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
  91. data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
  92. data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
  93. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
  94. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
  95. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
  96. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
  97. data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
  98. data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
  99. data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
  100. data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
  101. data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
  102. data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
  103. data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
  104. data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
  105. data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
  106. data/spec/bitcoin/key_spec.rb +128 -3
  107. data/spec/bitcoin/namecoin_spec.rb +182 -0
  108. data/spec/bitcoin/network_spec.rb +5 -3
  109. data/spec/bitcoin/node/command_api_spec.rb +376 -0
  110. data/spec/bitcoin/protocol/addr_spec.rb +2 -0
  111. data/spec/bitcoin/protocol/alert_spec.rb +2 -0
  112. data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
  113. data/spec/bitcoin/protocol/block_spec.rb +134 -39
  114. data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
  115. data/spec/bitcoin/protocol/inv_spec.rb +10 -8
  116. data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
  117. data/spec/bitcoin/protocol/ping_spec.rb +2 -0
  118. data/spec/bitcoin/protocol/tx_spec.rb +83 -17
  119. data/spec/bitcoin/protocol/version_spec.rb +7 -5
  120. data/spec/bitcoin/script/opcodes_spec.rb +412 -133
  121. data/spec/bitcoin/script/script_spec.rb +112 -13
  122. data/spec/bitcoin/spec_helper.rb +68 -0
  123. data/spec/bitcoin/storage/reorg_spec.rb +199 -0
  124. data/spec/bitcoin/storage/storage_spec.rb +337 -0
  125. data/spec/bitcoin/storage/validation_spec.rb +261 -0
  126. data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
  127. data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
  128. data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
  129. data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
  130. data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
  131. metadata +105 -51
  132. data/lib/bitcoin/storage/sequel.rb +0 -335
  133. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
  134. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
  135. data/spec/bitcoin/reorg_spec.rb +0 -129
  136. data/spec/bitcoin/storage_spec.rb +0 -229
@@ -0,0 +1,187 @@
1
+ $:.unshift( File.expand_path("../../lib", __FILE__) )
2
+ require 'eventmachine'
3
+ require 'bitcoin'
4
+ require 'socket'
5
+
6
+ class Bitcoin::Protocol::Parser; def log; stub=Object.new; def stub.method_missing(*a); end; stub; end; end
7
+
8
+
9
+ module SimpleNode
10
+ class Connection < EM::Connection
11
+
12
+ def on_tx(tx)
13
+ log.info { "received transaction: #{tx.hash}" }
14
+
15
+ puts tx.to_json
16
+
17
+ if tx.hash == @ask_tx
18
+ @args[:result] = tx
19
+ @args[:callback] ? (close_connection; @args[:callback].call(tx)) : EM.stop
20
+ end
21
+ end
22
+
23
+ def on_block(block)
24
+ log.info { "received block: #{block.hash}" }
25
+
26
+ puts block.to_json
27
+
28
+ if block.hash == @ask_block
29
+ if @ask_tx
30
+ if tx = block.tx.find{|tx| tx.hash == @ask_tx }
31
+ on_tx(tx)
32
+ else
33
+ log.info { "@ask_tx #{@ask_tx} not in @ask_block #{@ask_block}" }
34
+ @args[:result] = nil
35
+ @args[:callback] ? (close_connection; @args[:callback].call(nil)) : EM.stop
36
+ end
37
+ else
38
+ @args[:result] = block
39
+ @args[:callback] ? (close_connection; @args[:callback].call(block)) : EM.stop
40
+ end
41
+ end
42
+ end
43
+
44
+ def on_handshake_complete
45
+ return if @connected
46
+ @connected = true
47
+ log.info { "handshake complete" }
48
+
49
+ EM.add_timer(0.5){
50
+ if @ask_block
51
+ log.info { "ask for @ask_block: #{@ask_block}" }
52
+ send_data Bitcoin::Protocol.getdata_pkt(:block, [htb(@ask_block)])
53
+ else
54
+ if @ask_tx
55
+ log.info { "ask for @ask_tx: #{@ask_tx}" }
56
+ send_data Bitcoin::Protocol.getdata_pkt(:tx, [htb(@ask_tx)])
57
+ end
58
+ end
59
+ if @send_tx
60
+ tx = Bitcoin::P::Tx.from_json(File.read(@send_tx))
61
+ send_data(Bitcoin::Protocol.pkt('tx', tx.to_payload))
62
+ p [:sent, tx.hash]
63
+ end
64
+ }
65
+ end
66
+
67
+ def on_get_transaction(hash); end
68
+ def on_get_block(hash); end
69
+ def on_addr(addr); end
70
+ def on_inv_transaction(hash)
71
+ log.info { "peer told us about transaction: #{hth(hash)}" }
72
+ log.info { "asking peer for transaction: #{hth(hash)}" }
73
+ send_data Bitcoin::Protocol.getdata_pkt(:tx, [hash])
74
+ end
75
+
76
+ def on_inv_block(hash)
77
+ log.info { "peer told us about block: #{hth(hash)}" }
78
+ log.info { "asking peer for block: #{hth(hash)}" }
79
+ send_data Bitcoin::Protocol.getdata_pkt(:block, [hash])
80
+ end
81
+
82
+ def on_handshake_begin
83
+ log.info { "handshake started" }
84
+
85
+ version = Bitcoin::Protocol::Version.new({
86
+ :user_agent => "/Satoshi:0.8.1/",
87
+ :last_block => 0,
88
+ :from => "127.0.0.1:#{Bitcoin.network[:default_port]}",
89
+ :to => "#{@host}:#{@port}",
90
+ })
91
+
92
+ log.info { "sending version: Version:%d (%s) Block:%d" % version.fields.values_at(:version, :user_agent, :last_block) }
93
+ send_data(version.to_pkt)
94
+ end
95
+
96
+ def on_version(version)
97
+ @version ||= version
98
+ log.info { "received version: Version:%d (%s) Block:%d" % version.fields.values_at(:version, :user_agent, :last_block) }
99
+ send_data( Bitcoin::Protocol.verack_pkt )
100
+ on_handshake_complete
101
+ end
102
+
103
+ def initialize(host, port, node=nil, opts={})
104
+ set_host(host, port)
105
+ @node = node
106
+ @parser = Bitcoin::Protocol::Parser.new( self )
107
+
108
+ @args = opts
109
+ @ask_tx, @ask_block, @send_tx = opts.values_at(:ask_tx, :ask_block, :send_tx)
110
+ end
111
+
112
+ def receive_data(data); @parser.parse(data); end
113
+ def post_init; log.info { "peer connected" }; on_handshake_begin; end
114
+ def unbind; log.info { "peer disconnected" }; end
115
+ def set_host(host, port=8333); @host, @port = host, port; end
116
+
117
+ def log
118
+ return @log if @log
119
+ return (@log = (stub=Object.new; def stub.method_missing(*a); end; stub)) if @args[:nolog]
120
+ @logger ||= Bitcoin::Logger.create(:network, :info) unless @node.respond_to?(:log)
121
+ @log = Bitcoin::Logger::LogWrapper.new("#@host:#@port", @logger || @node.log)
122
+ end
123
+
124
+ def hth(h); h.unpack("H*")[0]; end
125
+ def htb(h); [h].pack("H*"); end
126
+
127
+
128
+ def self.connect(host, port, *args)
129
+ EM.connect(host, port, self, host, port, *args)
130
+ end
131
+
132
+ def self.connect_random_from_dns(seeds=[], count=1, *args)
133
+ seeds = Bitcoin.network[:dns_seeds] unless seeds.any?
134
+ if seeds.any?
135
+ seeds.sample(count).map{|dns|
136
+ host = IPSocket.getaddress(dns)
137
+ connect(host, Bitcoin.network[:default_port], *args)
138
+ }
139
+ else
140
+ raise "No DNS seeds available. Provide IP, configure seeds, or use different network."
141
+ end
142
+ end
143
+
144
+ def self.connect_known_nodes(count=1)
145
+ connect_random_from_dns(Bitcoin.network[:known_nodes], count)
146
+ end
147
+ end
148
+ end
149
+
150
+
151
+ if $0 == __FILE__
152
+
153
+ args = {
154
+ ask_tx: ARGV.find{|a| a[/tx=(.+)/, 1] } && $1,
155
+ ask_block: ARGV.find{|a| a[/block=(.+)/, 1] } && $1,
156
+ use_node: ARGV.find{|a| a[/node=(.+)/, 1] } && $1,
157
+ send_tx: ARGV.find{|a| a[/send_tx=(.+)/, 1] } && $1,
158
+ set_project: ARGV.find{|a| a[/project=(.+)/, 1] } && $1,
159
+ callback: proc{|i|
160
+ case i
161
+ when Bitcoin::Protocol::Block
162
+ puts "INFO network: SAVING @ask_block: #{i.hash}"
163
+ File.open("block-#{i.hash}.bin", 'wb'){|f| f.print i.payload }
164
+ File.open("block-#{i.hash}.json", 'wb'){|f| f.print i.to_json }
165
+ when Bitcoin::Protocol::Tx
166
+ puts "INFO network: SAVING @ask_tx: #{i.hash}"
167
+ File.open("tx-#{i.hash}.bin", 'wb'){|f| f.print i.payload }
168
+ File.open("tx-#{i.hash}.json", 'wb'){|f| f.print i.to_json }
169
+ end
170
+ EM.stop
171
+ }
172
+ }
173
+
174
+
175
+ EM.run do
176
+ if args[:set_project]
177
+ Bitcoin.network = args[:set_project].to_sym
178
+ p Bitcoin.network_project
179
+ end
180
+ if args[:use_node]
181
+ SimpleNode::Connection.connect_random_from_dns([args[:use_node]], 1, nil, args)
182
+ else
183
+ SimpleNode::Connection.connect_random_from_dns([], 1, nil, args)
184
+ end
185
+ end
186
+
187
+ end
@@ -7,7 +7,7 @@
7
7
  #
8
8
  # see Bitcoin::Protocol::Tx and Bitcoin::Script.
9
9
  # Note: For this to work, you need to have the transactions in your storage. see NODE.
10
-
10
+ # Note: There is also Bitcoin::Validation::Tx which validates a lot more than signatures.
11
11
 
12
12
  $:.unshift( File.expand_path("../../lib", __FILE__) )
13
13
  require 'bitcoin'
data/lib/bitcoin.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # encoding: ascii-8bit
1
2
  # Bitcoin Utils and Network Protocol in Ruby.
2
3
 
3
4
  require 'digest/sha2'
@@ -17,6 +18,9 @@ module Bitcoin
17
18
  autoload :Key, 'bitcoin/key'
18
19
  autoload :Config, 'bitcoin/config'
19
20
  autoload :Builder, 'bitcoin/builder'
21
+ autoload :Validation, 'bitcoin/validation'
22
+
23
+ autoload :Namecoin, 'bitcoin/namecoin'
20
24
 
21
25
  module Network
22
26
  autoload :ConnectionHandler, 'bitcoin/network/connection_handler'
@@ -43,6 +47,7 @@ module Bitcoin
43
47
  begin
44
48
  require name.to_s
45
49
  rescue LoadError
50
+ return false if name.to_s == "log4r"
46
51
  print "Cannot load #{opts[:exit] == false ? 'optional' : 'required'} dependency '#{name}'"
47
52
  (opts[:gem] == false) ? puts("") :
48
53
  puts(" - install with `gem install #{opts[:gem] || name}`")
@@ -55,9 +60,6 @@ module Bitcoin
55
60
 
56
61
  module Util
57
62
 
58
- def hth(h); h.unpack("H*")[0]; end
59
- def htb(h); [h].pack("H*"); end
60
-
61
63
  def address_version; Bitcoin.network[:address_version]; end
62
64
  def p2sh_version; Bitcoin.network[:p2sh_version]; end
63
65
 
@@ -206,6 +208,10 @@ module Bitcoin
206
208
  ).reverse.unpack("H*")[0]
207
209
  end
208
210
 
211
+ def bitcoin_byte_hash(bytes)
212
+ Digest::SHA256.digest(Digest::SHA256.digest(bytes))
213
+ end
214
+
209
215
  def bitcoin_mrkl(a, b); bitcoin_hash(b + a); end
210
216
 
211
217
  def block_hash(prev_block, mrkl_root, time, bits, nonce, ver)
@@ -214,16 +220,39 @@ module Bitcoin
214
220
  bitcoin_hash(h)
215
221
  end
216
222
 
223
+ # get merkle tree for given +tx+ list.
217
224
  def hash_mrkl_tree(tx)
225
+ return [nil] if tx != tx.uniq
218
226
  chunks = [ tx.dup ]
219
227
  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
- }
228
+ chunks << chunks.last.each_slice(2).map {|a, b|
229
+ Bitcoin.bitcoin_mrkl( a, b || a ) }
223
230
  end
224
231
  chunks.flatten
225
232
  end
226
233
 
234
+ # get merkle branch connecting given +target+ to the merkle root of +tx+ list
235
+ def hash_mrkl_branch(tx, target)
236
+ return [ nil ] if tx != tx.uniq
237
+ branch, chunks = [], [ tx.dup ]
238
+ while chunks.last.size >= 2
239
+ chunks << chunks.last.each_slice(2).map {|a, b|
240
+ hash = Bitcoin.bitcoin_mrkl( a, b || a )
241
+ next hash unless [a, b].include?(target)
242
+ branch << (a == target ? (b || a) : a)
243
+ target = hash
244
+ }
245
+ end
246
+ branch
247
+ end
248
+
249
+ # get merkle root from +branch+ and +target+.
250
+ def mrkl_branch_root(branch, target, idx)
251
+ branch.map do |hash|
252
+ a, b = *( idx & 1 == 0 ? [target, hash] : [hash, target] )
253
+ idx >>= 1; target = Bitcoin.bitcoin_mrkl( a, b )
254
+ end.last
255
+ end
227
256
 
228
257
  def sign_data(key, data)
229
258
  key.dsa_sign_asn1(data)
@@ -235,7 +264,7 @@ module Bitcoin
235
264
  key.dsa_verify_asn1(hash, signature)
236
265
  rescue OpenSSL::PKey::ECError, OpenSSL::PKey::EC::Point::Error
237
266
  false
238
- end
267
+ end
239
268
 
240
269
  def open_key(private_key, public_key=nil)
241
270
  key = bitcoin_elliptic_curve
@@ -249,6 +278,30 @@ module Bitcoin
249
278
  Bitcoin::OpenSSL_EC.regenerate_key(private_key)[1]
250
279
  end
251
280
 
281
+ def bitcoin_signed_message_hash(message)
282
+ # TODO: this will fail horribly on messages with len > 255. It's a cheap implementation of Bitcoin's CDataStream.
283
+ data = "\x18Bitcoin Signed Message:\n" + [message.bytesize].pack("C") + message
284
+ Digest::SHA256.digest(Digest::SHA256.digest(data))
285
+ end
286
+
287
+ def sign_message(private_key_hex, public_key_hex, message)
288
+ hash = bitcoin_signed_message_hash(message)
289
+ signature = Bitcoin::OpenSSL_EC.sign_compact(hash, private_key_hex, public_key_hex)
290
+ { 'address' => pubkey_to_address(public_key_hex), 'message' => message, 'signature' => [ signature ].pack("m0") }
291
+ end
292
+
293
+ def verify_message(address, signature, message)
294
+ hash = bitcoin_signed_message_hash(message)
295
+ signature = signature.unpack("m0")[0] rescue nil # decode base64
296
+ raise "invalid address" unless valid_address?(address)
297
+ raise "malformed base64 encoding" unless signature
298
+ raise "malformed signature" unless signature.bytesize == 65
299
+ pubkey = Bitcoin::OpenSSL_EC.recover_compact(hash, signature)
300
+ pubkey_to_address(pubkey) == address if pubkey
301
+ rescue Exception => ex
302
+ p [ex.message, ex.backtrace]; false
303
+ end
304
+
252
305
 
253
306
  RETARGET_INTERVAL = 2016
254
307
 
@@ -283,6 +336,13 @@ module Bitcoin
283
336
  block_hashes_to_win(target_nbits) / hashes_per_second
284
337
  end
285
338
 
339
+ # average mining time (in days) using Mh/s to get btc
340
+ def block_average_mining_time(block_nbits, block_height, mega_hashes_per_second, target_btc=1.0)
341
+ seconds = block_average_hashing_time(block_nbits, mega_hashes_per_second * 1_000_000)
342
+ reward = block_creation_reward(block_height) / Bitcoin::COIN # satoshis to btc
343
+ (days = seconds / 60 / 60 / 24) * (target_btc / reward)
344
+ end
345
+
286
346
  # shows the total number of Bitcoins in circulation, reward era and reward in that era.
287
347
  def blockchain_total_btc(height)
288
348
  reward, interval = 5000000000, 210000
@@ -301,6 +361,21 @@ module Bitcoin
301
361
  end
302
362
  end
303
363
 
364
+ extend Util
365
+
366
+
367
+ module BinaryExtensions
368
+ def hth; unpack("H*")[0]; end
369
+ def reverse_hth; reverse.hth; end
370
+ def htb; [self].pack("H*"); end
371
+ def htb_reverse; htb.reverse; end
372
+ end
373
+
374
+ class ::String
375
+ include Bitcoin::BinaryExtensions
376
+ end
377
+
378
+
304
379
  module ::OpenSSL
305
380
  class BN
306
381
  def self.from_hex(hex); new(hex, 16); end
@@ -310,6 +385,7 @@ module Bitcoin
310
385
  class PKey::EC
311
386
  def private_key_hex; private_key.to_hex.rjust(64, '0'); end
312
387
  def public_key_hex; public_key.to_hex.rjust(130, '0'); end
388
+ def pubkey_compressed?; public_key.group.point_conversion_form == :compressed; end
313
389
  end
314
390
  class PKey::EC::Point
315
391
  def self.from_hex(group, hex)
@@ -322,49 +398,263 @@ module Bitcoin
322
398
 
323
399
  autoload :OpenSSL_EC, "bitcoin/ffi/openssl"
324
400
 
325
-
326
- extend Util
327
-
328
401
  @network = :bitcoin
329
402
 
330
403
  def self.network
331
404
  NETWORKS[@network]
332
405
  end
333
406
 
407
+ def self.network_name
408
+ @network
409
+ end
410
+
411
+ def self.network_project
412
+ @network_project
413
+ end
414
+
334
415
  def self.network= name
416
+ raise "Network descriptor '#{name}' not found." unless NETWORKS[name.to_sym]
335
417
  @network = name.to_sym
418
+ @network_project = network[:project] rescue nil
419
+ Bitcoin::Namecoin.load if namecoin?
420
+ @network
336
421
  end
337
422
 
423
+ [:bitcoin, :namecoin, :litecoin, :freicoin].each do |n|
424
+ instance_eval "def #{n}?; network_project == :#{n}; end"
425
+ end
426
+
427
+
428
+ CENT = 1_000_000
429
+ COIN = 100_000_000
430
+ MAX_BLOCK_SIZE = 1_000_000
431
+ MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2
432
+ MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50
433
+ MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100
434
+
435
+ MIN_FEE_MODE = [ :block, :relay, :send ]
436
+
338
437
  NETWORKS = {
438
+
339
439
  :bitcoin => {
440
+ :project => :bitcoin,
340
441
  :magic_head => "\xF9\xBE\xB4\xD9",
341
442
  :address_version => "00",
342
443
  :p2sh_version => "05",
343
444
  :privkey_version => "80",
344
445
  :default_port => 8333,
345
- :dns_seeds => ["bitseed.xf2.org", "dnsseed.bluematt.me",
346
- "dnsseed.bitcoin.dashjr.org", "seed.bitcoin.sipa.be"],
446
+ :protocol_version => 70001,
447
+ :coinbase_maturity => 100,
448
+ :retarget_interval => 2016,
449
+ :retarget_time => 1209600, # 2 weeks
450
+ :max_money => 21_000_000 * COIN,
451
+ :min_tx_fee => 50_000,
452
+ :min_relay_tx_fee => 10_000,
453
+ :dns_seeds => [
454
+ "seed.bitcoin.sipa.be",
455
+ "dnsseed.bluematt.me",
456
+ "dnsseed.bitcoin.dashjr.org",
457
+ "bitseed.xf2.org",
458
+ ],
347
459
  :genesis_hash => "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
348
460
  :proof_of_work_limit => 0x1d00ffff,
461
+ :alert_pubkeys => ["04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284"],
349
462
  :known_nodes => [
350
463
  'relay.eligius.st',
351
464
  'mining.bitcoin.cz',
352
- 'bitcoins.lc',
353
465
  'blockchain.info',
354
466
  'blockexplorer.com',
355
- ]
467
+ ],
468
+ :checkpoints => {
469
+ 11111 => "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d",
470
+ 33333 => "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6",
471
+ 74000 => "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20",
472
+ 105000 => "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97",
473
+ 134444 => "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe",
474
+ 168000 => "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763",
475
+ 193000 => "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317",
476
+ 210000 => "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e",
477
+ 216116 => "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e",
478
+ 225430 => "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932",
479
+ }
356
480
  },
481
+
357
482
  :testnet => {
483
+ :project => :bitcoin,
358
484
  :magic_head => "\xFA\xBF\xB5\xDA",
359
485
  :address_version => "6f",
360
486
  :p2sh_version => "c4",
361
487
  :privkey_version => "ef",
362
488
  :default_port => 18333,
363
- :dns_seeds => ["testseed.bitcoin.interesthings.de"],
489
+ :max_money => 21_000_000 * COIN,
490
+ :dns_seeds => [ "testseed.bitcoin.interesthings.de" ],
364
491
  :genesis_hash => "00000007199508e34a9ff81e6ec0c477a4cccff2a4767a8eee39c11db367b008",
365
492
  :proof_of_work_limit => 0x1d07fff8,
366
- :known_nodes => []
367
- }
493
+ :alert_pubkeys => ["04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"],
494
+ :known_nodes => [],
495
+ :checkpoints => {}
496
+ },
497
+
498
+ :testnet3 => {
499
+ :project => :bitcoin,
500
+ :magic_head => "\x0b\x11\x09\x07",
501
+ :address_version => "6f",
502
+ :p2sh_version => "c4",
503
+ :privkey_version => "ef",
504
+ :default_port => 18333,
505
+ :protocol_version => 70001,
506
+ :coinbase_maturity => 100,
507
+ :retarget_interval => 2016,
508
+ :retarget_time => 1209600, # 2 weeks
509
+ :max_money => 21_000_000 * COIN,
510
+ :min_tx_fee => 50_000,
511
+ :min_relay_tx_fee => 10_000,
512
+ :dns_seeds => [
513
+ "testnet-seed.bitcoin.petertodd.org",
514
+ "testnet-seed.bluematt.me",
515
+ ],
516
+ :genesis_hash => "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
517
+ :proof_of_work_limit => 0x1d07fff8,
518
+ :alert_pubkeys => ["04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"],
519
+ :known_nodes => [],
520
+ :checkpoints => {
521
+ # 542 contains invalid transaction
522
+ 542 => "0000000083c1f82cf72c6724f7a317325806384b06408bce7a4327f418dfd5ad",
523
+ 71018 => "000000000010dd93dc55541116b2744eb8f4c3b706df6e8512d231a03fb9e435",
524
+ }
525
+ },
526
+
527
+ :litecoin => {
528
+ :project => :litecoin,
529
+ :magic_head => "\xfb\xc0\xb6\xdb",
530
+ :address_version => "30",
531
+ :p2sh_version => "05",
532
+ :privkey_version => "ef",
533
+ :default_port => 9333,
534
+ :protocol_version => 60002,
535
+ :max_money => 84_000_000 * COIN,
536
+ :min_tx_fee => 2_000_000,
537
+ :coinbase_maturity => 100,
538
+ :retarget_interval => 2016,
539
+ :retarget_time => 302400, # 3.5 days
540
+ :min_relay_tx_fee => 1_000_000,
541
+ :dns_seeds => [
542
+ "dnsseed.litecointools.com",
543
+ "dnsseed.litecoinpool.org",
544
+ "dnsseed.ltc.xurious.com",
545
+ "dnsseed.koin-project.com",
546
+ "dnsseed.weminemnc.com",
547
+ ],
548
+ :genesis_hash => "12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2",
549
+ :proof_of_work_limit => 0,
550
+ :alert_pubkeys => [],
551
+ :known_nodes => [],
552
+ :checkpoints => {
553
+ 1 => "80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f",
554
+ 2 => "13957807cdd1d02f993909fa59510e318763f99a506c4c426e3b254af09f40d7",
555
+ 1500 => "841a2965955dd288cfa707a755d05a54e45f8bd476835ec9af4402a2b59a2967",
556
+ 4032 => "9ce90e427198fc0ef05e5905ce3503725b80e26afd35a987965fd7e3d9cf0846",
557
+ 8064 => "eb984353fc5190f210651f150c40b8a4bab9eeeff0b729fcb3987da694430d70",
558
+ 16128 => "602edf1859b7f9a6af809f1d9b0e6cb66fdc1d4d9dcd7a4bec03e12a1ccd153d",
559
+ 23420 => "d80fdf9ca81afd0bd2b2a90ac3a9fe547da58f2530ec874e978fce0b5101b507",
560
+ 50000 => "69dc37eb029b68f075a5012dcc0419c127672adb4f3a32882b2b3e71d07a20a6",
561
+ 80000 => "4fcb7c02f676a300503f49c764a89955a8f920b46a8cbecb4867182ecdb2e90a",
562
+ 120000 => "bd9d26924f05f6daa7f0155f32828ec89e8e29cee9e7121b026a7a3552ac6131",
563
+ 161500 => "dbe89880474f4bb4f75c227c77ba1cdc024991123b28b8418dbbf7798471ff43",
564
+ 179620 => "2ad9c65c990ac00426d18e446e0fd7be2ffa69e9a7dcb28358a50b2b78b9f709",
565
+ 240000 => "7140d1c4b4c2157ca217ee7636f24c9c73db39c4590c4e6eab2e3ea1555088aa",
566
+ 383640 => "2b6809f094a9215bafc65eb3f110a35127a34be94b7d0590a096c3f126c6f364",
567
+ }
568
+ },
569
+
570
+ :litecoin_testnet => {
571
+ :project => :litecoin,
572
+ :magic_head => "\xfc\xc1\xb7\xdc",
573
+ :address_version => "6f",
574
+ :p2sh_version => "c4",
575
+ :privkey_version => "ef",
576
+ :default_port => 19333,
577
+ :protocol_version => 60002,
578
+ :min_tx_fee => 2_000_000,
579
+ :min_relay_tx_fee => 1_000_000,
580
+ :coinbase_maturity => 100,
581
+ :retarget_interval => 2016,
582
+ :retarget_time => 302400, # 3.5 days
583
+ :max_money => 84_000_000 * COIN,
584
+ :dns_seeds => [
585
+ "testnet-seed.litecointools.com",
586
+ "testnet-seed.weminemnc.com",
587
+ ],
588
+ :genesis_hash => "f5ae71e26c74beacc88382716aced69cddf3dffff24f384e1808905e0188f68f",
589
+ :proof_of_work_limit => 0,
590
+ :alert_pubkeys => [],
591
+ :known_nodes => [],
592
+ :checkpoints => {}
593
+ },
594
+
595
+
596
+ :freicoin => {
597
+ :project => :freicoin,
598
+ :magic_head => "\x2c\xfe\x7e\x6d",
599
+ :address_version => "00",
600
+ :p2sh_version => "05",
601
+ :privkey_version => "80",
602
+ :default_port => 8639,
603
+ :protocol_version => 60002,
604
+ :max_money => 21_000_000 * COIN,
605
+ :min_tx_fee => 50_000,
606
+ :min_relay_tx_fee => 10_000,
607
+ :dns_seeds => [ "seed.freico.in", "fledge.freico.in" ],
608
+ :genesis_hash => "000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c",
609
+ :proof_of_work_limit => 0,
610
+ :alert_pubkeys => [],
611
+ :known_nodes => [],
612
+ :checkpoints => {
613
+ 10080 => "00000000003ff9c4b806639ec4376cc9acafcdded0e18e9dbcc2fc42e8e72331",
614
+ 15779 => "000000000003eb31742b35f5efd8ffb5cdd19dcd8e82cdaad90e592c450363b6",
615
+ }
616
+ },
617
+
618
+ :namecoin => {
619
+ :project => :namecoin,
620
+ :magic_head => "\xF9\xBE\xB4\xFE",
621
+ :address_version => "34",
622
+ :default_port => 8334,
623
+ :protocol_version => 35000,
624
+ :max_money => 21_000_000 * COIN,
625
+ :min_tx_fee => 50_000,
626
+ :min_relay_tx_fee => 10_000,
627
+ :dns_seeds => [],
628
+ :genesis_hash => "000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770",
629
+ :proof_of_work_limit => 0x1d00ffff,
630
+ :known_nodes => ["bitcoin.tunl.in", "webbtc.com", "178.32.31.41",
631
+ "78.47.86.43", "69.164.206.88", ""],
632
+ :checkpoints => {
633
+ 0 => "000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770",
634
+ 19200 => "d8a7c3e01e1e95bcee015e6fcc7583a2ca60b79e5a3aa0a171eddd344ada903d",
635
+ 24000 => "425ab0983cf04f43f346a4ca53049d0dc2db952c0a68eb0b55c3bb64108d5371",
636
+ 97778 => "7553b1e43da01cfcda4335de1caf623e941d43894bd81c2af27b6582f9d83c6f",
637
+ }
638
+ },
639
+
640
+ :namecoin_testnet => {
641
+ :project => :namecoin,
642
+ :magic_head => "\xFA\xBF\xB5\xFE",
643
+ :address_version => "34",
644
+ :default_port => 18334,
645
+ :protocol_version => 35000,
646
+ :min_tx_fee => 50_000,
647
+ :min_relay_tx_fee => 10_000,
648
+ :max_money => 21_000_000 * COIN,
649
+ :dns_seeds => [],
650
+ :genesis_hash => "00000001f8ab0d14bceaeb50d163b0bef15aecf62b87bd5f5c864d37f201db97",
651
+ :proof_of_work_limit => 0x1d00ffff,
652
+ :known_nodes => ["178.32.31.41"],
653
+ :checkpoints => {
654
+ 0 => "000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770",
655
+
656
+ }
657
+ },
368
658
  }
369
-
659
+
370
660
  end