bitcoin-ruby 0.0.1 → 0.0.2

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