bitcoin-ruby 0.0.5 → 0.0.6
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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +2 -2
- data/COPYING +1 -1
- data/Gemfile +5 -11
- data/README.rdoc +11 -5
- data/Rakefile +5 -0
- data/bin/bitcoin_node +11 -29
- data/bin/bitcoin_node_cli +81 -0
- data/bin/bitcoin_wallet +9 -6
- data/doc/NODE.rdoc +79 -26
- data/examples/bbe_verify_tx.rb +1 -1
- data/examples/index_nhash.rb +24 -0
- data/examples/reindex_p2sh_addrs.rb +44 -0
- data/lib/bitcoin.rb +135 -20
- data/lib/bitcoin/builder.rb +233 -63
- data/lib/bitcoin/key.rb +89 -16
- data/lib/bitcoin/litecoin.rb +13 -11
- data/lib/bitcoin/namecoin.rb +5 -4
- data/lib/bitcoin/network/command_client.rb +23 -13
- data/lib/bitcoin/network/command_handler.rb +336 -131
- data/lib/bitcoin/network/connection_handler.rb +14 -13
- data/lib/bitcoin/network/node.rb +61 -20
- data/lib/bitcoin/protocol.rb +5 -1
- data/lib/bitcoin/protocol/block.rb +15 -3
- data/lib/bitcoin/protocol/parser.rb +3 -3
- data/lib/bitcoin/protocol/tx.rb +82 -20
- data/lib/bitcoin/protocol/txin.rb +7 -0
- data/lib/bitcoin/protocol/txout.rb +12 -9
- data/lib/bitcoin/script.rb +329 -75
- data/lib/bitcoin/storage/dummy/dummy_store.rb +23 -4
- data/lib/bitcoin/storage/models.rb +6 -11
- data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +14 -0
- data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +31 -0
- data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +16 -0
- data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +31 -0
- data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +56 -0
- data/lib/bitcoin/storage/sequel/sequel_store.rb +168 -70
- data/lib/bitcoin/storage/storage.rb +161 -97
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +1 -1
- data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +14 -0
- data/lib/bitcoin/storage/utxo/utxo_store.rb +25 -12
- data/lib/bitcoin/validation.rb +87 -56
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bitcoin_spec.rb +38 -0
- data/spec/bitcoin/builder_spec.rb +177 -0
- data/spec/bitcoin/fixtures/litecoin-tx-f5aa30f574e3b6f1a3d99c07a6356ba812aabb9661e1d5f71edff828cbd5c996.json +259 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-265322.bin +0 -0
- data/spec/bitcoin/fixtures/tx-0295028ef826b2a188409cb905b631faebb9bb3cdf14510571c5f4bd8591338f.json +64 -0
- data/spec/bitcoin/fixtures/tx-03339a725007a279484fb6f5361f522dd1cf4d0923d30e6b973290dba4275f92.json +64 -0
- data/spec/bitcoin/fixtures/tx-0ce7e5238fbdb6c086cf1b384b21b827e91cc23f360417265874a5a0d86ce367.json +64 -0
- data/spec/bitcoin/fixtures/tx-0ef34c49f630aea17df0080728b0fc67bf5f87fbda936934a4b11b4a69d7821e.json +64 -0
- data/spec/bitcoin/fixtures/tx-1129d2a8bd5bb3a81e54dc96a90f1f6b2544575748caa17243470935c5dd91b7.json +28 -0
- data/spec/bitcoin/fixtures/tx-19aa42fee0fa57c45d3b16488198b27caaacc4ff5794510d0c17f173f05587ff.json +23 -0
- data/spec/bitcoin/fixtures/tx-1a4f3b9dc4494aeedeb39f30dd37e60541b2abe3ed4977992017cc0ad4f44956.json +64 -0
- data/spec/bitcoin/fixtures/tx-1f9191dcf2b1844ca28c6ef4b969e1d5fab70a5e3c56b7007949e55851cb0c4f.json +64 -0
- data/spec/bitcoin/fixtures/tx-22cd5fef23684d7b304e119bedffde6f54538d3d54a5bfa237e20dc2d9b4b5ad.json +64 -0
- data/spec/bitcoin/fixtures/tx-2958fb00b4fd6fe0353503b886eb9a193d502f4fd5fc042d5e03216ba918bbd6.json +64 -0
- data/spec/bitcoin/fixtures/tx-29f277145749ad6efbed3ae6ce301f8d33c585ec26b7c044ad93c2f866e9e942.json +64 -0
- data/spec/bitcoin/fixtures/tx-2c5e5376c20e9cc78d0fb771730e5d840cc2096eff0ef045b599fe92475ace1c.json +28 -0
- data/spec/bitcoin/fixtures/tx-2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1.json +30 -0
- data/spec/bitcoin/fixtures/tx-326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e.json +23 -0
- data/spec/bitcoin/fixtures/tx-345bed8785c3282a264ffb0dbee61cde54854f10e16f1b3e75b7f2d9f62946f2.json +64 -0
- data/spec/bitcoin/fixtures/tx-39ba7440b7103557560cc8ce258009936796485aaf8b478e66ab4cb97c66e31b.json +32 -0
- data/spec/bitcoin/fixtures/tx-3a04d57a833367f1655cc5ec3beb587888ef4977a86caa8c8ad4ba7cc717eae7.json +64 -0
- data/spec/bitcoin/fixtures/tx-4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9.json +38 -0
- data/spec/bitcoin/fixtures/tx-46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa.json +23 -0
- data/spec/bitcoin/fixtures/tx-5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f.json +30 -0
- data/spec/bitcoin/fixtures/tx-62d9a565bd7b5344c5352e3e9e5f40fa4bbd467fa19c87357216ec8777ba1cce.json +64 -0
- data/spec/bitcoin/fixtures/tx-6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190.json +23 -0
- data/spec/bitcoin/fixtures/tx-6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba.json +27 -0
- data/spec/bitcoin/fixtures/tx-6aaf18b9f1283b939d8e5d40ff5f8a435229f4178372659cc3a0bce4e262bf78.json +28 -0
- data/spec/bitcoin/fixtures/tx-6b48bba6f6d2286d7ec0883c0fc3085955090813a4c94980466611c798b868cc.json +64 -0
- data/spec/bitcoin/fixtures/tx-70cfbc6690f9ab46712db44e3079ac227962b2771a9341d4233d898b521619ef.json +40 -0
- data/spec/bitcoin/fixtures/tx-7a1a9db42f065f75110fcdb1bc415549c8ef7670417ba1d35a67f1b8adc562c1.json +64 -0
- data/spec/bitcoin/fixtures/tx-9a768fc7d0c4bdc86e25154357ef7c0063ca21310e5740a2f12f90b7455184a7.json +64 -0
- data/spec/bitcoin/fixtures/tx-9cad8d523a0694f2509d092c39cebc8046adae62b4e4297102d568191d9478d8.json +64 -0
- data/spec/bitcoin/fixtures/tx-9e052eb694bd7e15906433f064dff0161a12fd325c1124537766377004023c6f.json +64 -0
- data/spec/bitcoin/fixtures/tx-a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944.json +23 -0
- data/spec/bitcoin/fixtures/tx-aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8.json +23 -0
- data/spec/bitcoin/fixtures/tx-ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742.json +27 -0
- data/spec/bitcoin/fixtures/tx-ad4bcf3241e5d2ad140564e20db3567d41594cf4c2012433fe46a2b70e0d87b8.json +64 -0
- data/spec/bitcoin/fixtures/tx-b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9.json +27 -0
- data/spec/bitcoin/fixtures/tx-b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d.json +28 -0
- data/spec/bitcoin/fixtures/tx-bbca0628c42cb8bf7c3f4b2ad688fa56da5308dd2a10255da89fb1f46e6e413d.json +36 -0
- data/spec/bitcoin/fixtures/tx-bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224.json +23 -0
- data/spec/bitcoin/fixtures/tx-c192b74844e4837a34c4a5a97b438f1c111405b01b99e2d12b7c96d07fc74c04.json +28 -0
- data/spec/bitcoin/fixtures/tx-e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009.json +406 -0
- data/spec/bitcoin/fixtures/tx-eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb.json +35 -0
- data/spec/bitcoin/fixtures/tx-fee1b9b85531c8fb6cd7831f83490c7f2aa768b6eefe29854ef5e89ce7b9ecb1.json +64 -0
- data/spec/bitcoin/fixtures/txscript-invalid-too-many-sigops-followed-by-invalid-pushdata.bin +1 -0
- data/spec/bitcoin/helpers/fake_blockchain.rb +183 -0
- data/spec/bitcoin/key_spec.rb +79 -8
- data/spec/bitcoin/namecoin_spec.rb +1 -1
- data/spec/bitcoin/node/command_api_spec.rb +373 -86
- data/spec/bitcoin/performance/storage_spec.rb +41 -0
- data/spec/bitcoin/protocol/addr_spec.rb +7 -5
- data/spec/bitcoin/protocol/aux_pow_spec.rb +1 -0
- data/spec/bitcoin/protocol/block_spec.rb +6 -0
- data/spec/bitcoin/protocol/tx_spec.rb +184 -1
- data/spec/bitcoin/protocol/txin_spec.rb +27 -0
- data/spec/bitcoin/protocol/txout_spec.rb +27 -0
- data/spec/bitcoin/script/opcodes_spec.rb +74 -3
- data/spec/bitcoin/script/script_spec.rb +271 -0
- data/spec/bitcoin/spec_helper.rb +34 -6
- data/spec/bitcoin/storage/models_spec.rb +104 -0
- data/spec/bitcoin/storage/reorg_spec.rb +42 -11
- data/spec/bitcoin/storage/storage_spec.rb +58 -15
- data/spec/bitcoin/storage/validation_spec.rb +44 -14
- data/spec/bitcoin/wallet/keygenerator_spec.rb +6 -3
- data/spec/bitcoin/wallet/keystore_spec.rb +3 -3
- data/spec/bitcoin/wallet/wallet_spec.rb +87 -89
- metadata +117 -11
|
@@ -41,7 +41,7 @@ module Bitcoin::Network
|
|
|
41
41
|
@latency_ms = nil
|
|
42
42
|
@lock = Monitor.new
|
|
43
43
|
@last_getblocks = [] # the last few getblocks messages received
|
|
44
|
-
rescue
|
|
44
|
+
rescue
|
|
45
45
|
log.fatal { "Error in #initialize" }
|
|
46
46
|
p $!; puts $@; exit
|
|
47
47
|
end
|
|
@@ -51,15 +51,16 @@ module Bitcoin::Network
|
|
|
51
51
|
if incoming?
|
|
52
52
|
begin_handshake
|
|
53
53
|
end
|
|
54
|
-
rescue
|
|
54
|
+
rescue
|
|
55
55
|
log.fatal { "Error in #post_init" }
|
|
56
56
|
p $!; puts *$@
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
# only called for outgoing connection
|
|
60
60
|
def connection_completed
|
|
61
|
+
@connection_completed = true
|
|
61
62
|
begin_handshake
|
|
62
|
-
rescue
|
|
63
|
+
rescue
|
|
63
64
|
log.fatal { "Error in #connection_completed" }
|
|
64
65
|
p $!; puts *$@
|
|
65
66
|
end
|
|
@@ -75,8 +76,8 @@ module Bitcoin::Network
|
|
|
75
76
|
|
|
76
77
|
# connection closed; notify listeners and cleanup connection from node
|
|
77
78
|
def unbind
|
|
78
|
-
log.info { "Disconnected" }
|
|
79
|
-
@node.push_notification(:connection,
|
|
79
|
+
log.info { (outgoing? && !@connection_completed) ? "Connection failed" : "Disconnected" }
|
|
80
|
+
@node.push_notification(:connection, {type: :disconnected, host: @host, port: @port})
|
|
80
81
|
@state = :disconnected
|
|
81
82
|
@node.connections.delete(self)
|
|
82
83
|
end
|
|
@@ -90,9 +91,8 @@ module Bitcoin::Network
|
|
|
90
91
|
log.info { "Established #{@direction} connection" }
|
|
91
92
|
@node.connections << self
|
|
92
93
|
@state = :handshake
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
rescue Exception
|
|
94
|
+
send_version
|
|
95
|
+
rescue
|
|
96
96
|
log.fatal { "Error in #begin_handshake" }
|
|
97
97
|
p $!; puts *$@
|
|
98
98
|
end
|
|
@@ -103,9 +103,10 @@ module Bitcoin::Network
|
|
|
103
103
|
log.debug { 'Handshake completed' }
|
|
104
104
|
@state = :connected
|
|
105
105
|
@started = Time.now
|
|
106
|
-
@node.push_notification(:connection,
|
|
106
|
+
@node.push_notification(:connection, info.merge(type: :connected))
|
|
107
107
|
@node.addrs << addr
|
|
108
108
|
end
|
|
109
|
+
send_data P::Addr.pkt(@node.addr) if @node.config[:announce]
|
|
109
110
|
end
|
|
110
111
|
|
|
111
112
|
# received +inv_tx+ message for given +hash+.
|
|
@@ -223,15 +224,15 @@ module Bitcoin::Network
|
|
|
223
224
|
|
|
224
225
|
return unless depth && depth <= @node.store.get_depth
|
|
225
226
|
range = (depth+1..depth+500)
|
|
226
|
-
blocks = @node.store.db[:blk].where(chain: 0, depth: range).select(:hash).all
|
|
227
|
-
[@node.store.db[:blk].select(:hash)[chain: 0, depth: depth+502]]
|
|
227
|
+
blocks = @node.store.db[:blk].where(chain: 0, depth: range).order(:depth).select(:hash).all
|
|
228
228
|
send_inv(:block, *blocks.map {|b| b[:hash].hth })
|
|
229
229
|
end
|
|
230
230
|
|
|
231
231
|
# received +getaddr+ message.
|
|
232
232
|
# send +addr+ message with peer addresses back.
|
|
233
233
|
def on_getaddr
|
|
234
|
-
addrs = @node.
|
|
234
|
+
addrs = @node.config[:announce] ? [@node.addr] : []
|
|
235
|
+
addrs += @node.addrs.select{|a| a.time > Time.now.to_i - 10800 }.shuffle[0..250]
|
|
235
236
|
log.debug { "<< addr (#{addrs.size})" }
|
|
236
237
|
send_data P::Addr.pkt(*addrs)
|
|
237
238
|
end
|
|
@@ -311,7 +312,7 @@ module Bitcoin::Network
|
|
|
311
312
|
send_data(Protocol.ping_pkt(@ping_nonce))
|
|
312
313
|
else
|
|
313
314
|
# set latency to 5 seconds, terrible but this version should be obsolete now
|
|
314
|
-
@latency_ms = (5*1000)
|
|
315
|
+
@latency_ms = (5*1000)
|
|
315
316
|
log.debug { "<< ping" }
|
|
316
317
|
send_data(Protocol.ping_pkt)
|
|
317
318
|
end
|
data/lib/bitcoin/network/node.rb
CHANGED
|
@@ -50,11 +50,16 @@ module Bitcoin::Network
|
|
|
50
50
|
|
|
51
51
|
DEFAULT_CONFIG = {
|
|
52
52
|
:network => :bitcoin,
|
|
53
|
-
:listen => ["0.0.0.0",
|
|
53
|
+
:listen => ["0.0.0.0", nil],
|
|
54
54
|
:connect => [],
|
|
55
55
|
:command => ["127.0.0.1", 9999],
|
|
56
56
|
:storage => "utxo::sqlite://~/.bitcoin-ruby/<network>/blocks.db",
|
|
57
|
+
:announce => false,
|
|
58
|
+
:external_port => nil,
|
|
57
59
|
:mode => :full,
|
|
60
|
+
:cache_head => true,
|
|
61
|
+
:index_nhash => false,
|
|
62
|
+
:index_p2sh_type => false,
|
|
58
63
|
:dns => true,
|
|
59
64
|
:epoll_limit => 10000,
|
|
60
65
|
:epoll_user => nil,
|
|
@@ -102,8 +107,9 @@ module Bitcoin::Network
|
|
|
102
107
|
def set_store
|
|
103
108
|
backend, config = @config[:storage].split('::')
|
|
104
109
|
@store = Bitcoin::Storage.send(backend, {
|
|
105
|
-
db: config, mode: @config[:mode], cache_head:
|
|
106
|
-
skip_validation: @config[:skip_validation],
|
|
110
|
+
db: config, mode: @config[:mode], cache_head: @config[:cache_head],
|
|
111
|
+
skip_validation: @config[:skip_validation], index_nhash: @config[:index_nhash],
|
|
112
|
+
index_p2sh_type: @config[:index_p2sh_type],
|
|
107
113
|
log_level: @config[:log][:storage]}, ->(locator) {
|
|
108
114
|
peer = @connections.select(&:connected?).sample
|
|
109
115
|
peer.send_getblocks(locator)
|
|
@@ -251,6 +257,31 @@ module Bitcoin::Network
|
|
|
251
257
|
self.stop
|
|
252
258
|
end
|
|
253
259
|
|
|
260
|
+
subscribe(:block) do |blk, depth|
|
|
261
|
+
next unless @store.in_sync?
|
|
262
|
+
@log.debug { "Relaying block #{blk.hash}" }
|
|
263
|
+
@connections.each do |conn|
|
|
264
|
+
next unless conn.connected?
|
|
265
|
+
conn.send_inv(:block, blk.hash)
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
@store.subscribe(:block) do |blk, depth, chain|
|
|
270
|
+
if chain == 0 && blk.hash == @store.get_head.hash
|
|
271
|
+
@last_block_time = Time.now
|
|
272
|
+
push_notification(:block, [blk, depth])
|
|
273
|
+
blk.tx.each {|tx| @unconfirmed.delete(tx.hash) }
|
|
274
|
+
end
|
|
275
|
+
getblocks if chain == 2 && @store.in_sync?
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
@store.subscribe(:reorg) do |new_main, new_side|
|
|
279
|
+
@log.warn { "Reorg of #{new_side.size} blocks." }
|
|
280
|
+
new_main.each {|b| @log.debug { "new main: #{b}" } }
|
|
281
|
+
new_side.each {|b| @log.debug { "new side: #{b}" } }
|
|
282
|
+
push_notification(:reorg, [new_main, new_side])
|
|
283
|
+
end
|
|
284
|
+
|
|
254
285
|
end
|
|
255
286
|
end
|
|
256
287
|
|
|
@@ -378,27 +409,13 @@ module Bitcoin::Network
|
|
|
378
409
|
while obj = @queue.shift
|
|
379
410
|
begin
|
|
380
411
|
if obj[0].to_sym == :block
|
|
381
|
-
|
|
382
|
-
if res[1] == 0 && obj[1].hash == @store.get_head.hash
|
|
383
|
-
@last_block_time = Time.now
|
|
384
|
-
push_notification(:block, [obj[1], res[0]])
|
|
385
|
-
obj[1].tx.each {|tx| @unconfirmed.delete(tx.hash) }
|
|
386
|
-
end
|
|
387
|
-
getblocks if res[1] == 2 && @store.in_sync?
|
|
388
|
-
end
|
|
412
|
+
@store.new_block(obj[1])
|
|
389
413
|
else
|
|
390
414
|
drop = @unconfirmed.size - @config[:max][:unconfirmed] + 1
|
|
391
415
|
drop.times { @unconfirmed.shift } if drop > 0
|
|
392
416
|
unless @unconfirmed[obj[1].hash]
|
|
393
417
|
@unconfirmed[obj[1].hash] = obj[1]
|
|
394
418
|
push_notification(:tx, [obj[1], 0])
|
|
395
|
-
|
|
396
|
-
if @notifiers[:output]
|
|
397
|
-
obj[1].out.each do |out|
|
|
398
|
-
address = Bitcoin::Script.new(out.pk_script).get_address
|
|
399
|
-
push_notification(:output, [obj[1].hash, address, out.value, 0])
|
|
400
|
-
end
|
|
401
|
-
end
|
|
402
419
|
end
|
|
403
420
|
end
|
|
404
421
|
rescue Bitcoin::Validation::ValidationError
|
|
@@ -456,6 +473,12 @@ module Bitcoin::Network
|
|
|
456
473
|
@config[:listen][0]
|
|
457
474
|
end
|
|
458
475
|
|
|
476
|
+
# get the external port
|
|
477
|
+
# assume the same as local port if config option :external_port isn't given explicitly
|
|
478
|
+
def external_port
|
|
479
|
+
@config[:external_port] || @config[:listen][1] || Bitcoin.network[:default_port]
|
|
480
|
+
end
|
|
481
|
+
|
|
459
482
|
# push notification +message+ to +channel+
|
|
460
483
|
def push_notification channel, message
|
|
461
484
|
@notifiers[channel.to_sym].push(message) if @notifiers[channel.to_sym]
|
|
@@ -466,12 +489,30 @@ module Bitcoin::Network
|
|
|
466
489
|
# see CommandHandler for details.
|
|
467
490
|
def subscribe channel
|
|
468
491
|
@notifiers[channel.to_sym] ||= EM::Channel.new
|
|
469
|
-
@notifiers[channel.to_sym].subscribe
|
|
492
|
+
@notifiers[channel.to_sym].subscribe do |*data|
|
|
493
|
+
begin
|
|
494
|
+
yield(*data)
|
|
495
|
+
rescue
|
|
496
|
+
p $!; puts *$@
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
def unsubscribe channel, id
|
|
502
|
+
@notifiers[channel.to_sym].unsubscribe(id)
|
|
470
503
|
end
|
|
471
504
|
|
|
472
505
|
# should the node accept new incoming connections?
|
|
473
506
|
def accept_connections?
|
|
474
|
-
connections.select(&:incoming?).size
|
|
507
|
+
connections.select(&:incoming?).size < config[:max][:connections_in]
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# get Addr object for our own server
|
|
511
|
+
def addr
|
|
512
|
+
@addr = Bitcoin::P::Addr.new
|
|
513
|
+
@addr.time, @addr.service, @addr.ip, @addr.port =
|
|
514
|
+
Time.now.tv_sec, (1 << 0), external_ip, external_port
|
|
515
|
+
@addr
|
|
475
516
|
end
|
|
476
517
|
|
|
477
518
|
end
|
data/lib/bitcoin/protocol.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Bitcoin
|
|
|
12
12
|
|
|
13
13
|
# BIP 0031, pong message, is enabled for all versions AFTER this one
|
|
14
14
|
BIP0031_VERSION = 60000
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
autoload :TxIn, 'bitcoin/protocol/txin'
|
|
17
17
|
autoload :TxOut, 'bitcoin/protocol/txout'
|
|
18
18
|
autoload :Tx, 'bitcoin/protocol/tx'
|
|
@@ -161,6 +161,10 @@ module Bitcoin
|
|
|
161
161
|
pkt "getheaders", locator_payload(version, locator_hashes, stop_hash)
|
|
162
162
|
end
|
|
163
163
|
|
|
164
|
+
def self.headers_pkt(version, blocks)
|
|
165
|
+
pkt "headers", [pack_var_int(blocks.size), blocks.map{|block| block.block_header}.join].join
|
|
166
|
+
end
|
|
167
|
+
|
|
164
168
|
def self.read_binary_file(path)
|
|
165
169
|
File.open(path, 'rb'){|f| f.read }
|
|
166
170
|
end
|
|
@@ -73,7 +73,7 @@ module Bitcoin
|
|
|
73
73
|
@ver, @prev_block, @mrkl_root, @time, @bits, @nonce = buf.read(80).unpack("Va32a32VVV")
|
|
74
74
|
recalc_block_hash
|
|
75
75
|
|
|
76
|
-
if (@ver & BLOCK_VERSION_AUXPOW) > 0
|
|
76
|
+
if Bitcoin.network[:project] == :namecoin && (@ver & BLOCK_VERSION_AUXPOW) > 0
|
|
77
77
|
@aux_pow = AuxPow.new(nil)
|
|
78
78
|
@aux_pow.parse_data_from_io(buf)
|
|
79
79
|
end
|
|
@@ -99,6 +99,10 @@ module Bitcoin
|
|
|
99
99
|
@hash = Bitcoin.block_hash(@prev_block.reverse_hth, @mrkl_root.reverse_hth, @time, @bits, @nonce, @ver)
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
+
def recalc_block_scrypt_hash
|
|
103
|
+
@scrypt_hash = Bitcoin.block_scrypt_hash(@prev_block.reverse_hth, @mrkl_root.reverse_hth, @time, @bits, @nonce, @ver)
|
|
104
|
+
end
|
|
105
|
+
|
|
102
106
|
def recalc_mrkl_root
|
|
103
107
|
@mrkl_root = Bitcoin.hash_mrkl_tree( @tx.map(&:hash) ).last.htb_reverse
|
|
104
108
|
end
|
|
@@ -216,20 +220,28 @@ module Bitcoin
|
|
|
216
220
|
JSON.pretty_generate( h, options )
|
|
217
221
|
end
|
|
218
222
|
|
|
223
|
+
# block header binary output
|
|
224
|
+
def block_header
|
|
225
|
+
[@ver, @prev_block, @mrkl_root, @time, @bits, @nonce, Protocol.pack_var_int(0)].pack("Va32a32VVVa*")
|
|
226
|
+
end
|
|
227
|
+
|
|
219
228
|
# read binary block from a file
|
|
220
229
|
def self.from_file(path); new( Bitcoin::Protocol.read_binary_file(path) ); end
|
|
221
230
|
|
|
222
231
|
# read json block from a file
|
|
223
232
|
def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
|
|
224
233
|
|
|
234
|
+
# Get a Bitcoin::Validation object to validate this block. It needs a +store+
|
|
235
|
+
# to validate against, and optionally takes the +prev_block+ for optimization.
|
|
225
236
|
def validator(store, prev_block = nil)
|
|
226
237
|
@validator ||= Bitcoin::Validation::Block.new(self, store, prev_block)
|
|
227
238
|
end
|
|
228
239
|
|
|
229
240
|
# get the (statistical) amount of work that was needed to generate this block.
|
|
230
241
|
def block_work
|
|
231
|
-
target = Bitcoin.decode_compact_bits(@bits)
|
|
232
|
-
|
|
242
|
+
target = Bitcoin.decode_compact_bits(@bits).to_i(16)
|
|
243
|
+
return 0 if target <= 0
|
|
244
|
+
(2**256) / (target + 1)
|
|
233
245
|
end
|
|
234
246
|
|
|
235
247
|
end
|
|
@@ -111,8 +111,8 @@ module Bitcoin
|
|
|
111
111
|
|
|
112
112
|
# https://en.bitcoin.it/wiki/BIP_0035
|
|
113
113
|
def handle_mempool_request(payload)
|
|
114
|
-
return unless @version[:version] >= 60002 # Protocol version >= 60002
|
|
115
|
-
return unless (@version[:services] & Bitcoin::Protocol::Version::NODE_NETWORK) == 1 # NODE_NETWORK bit set in Services
|
|
114
|
+
return unless @version.fields[:version] >= 60002 # Protocol version >= 60002
|
|
115
|
+
return unless (@version.fields[:services] & Bitcoin::Protocol::Version::NODE_NETWORK) == 1 # NODE_NETWORK bit set in Services
|
|
116
116
|
@h.on_mempool if @h.respond_to?(:on_mempool)
|
|
117
117
|
end
|
|
118
118
|
|
|
@@ -139,7 +139,7 @@ module Bitcoin
|
|
|
139
139
|
def parse_buffer
|
|
140
140
|
head_magic = Bitcoin::network[:magic_head]
|
|
141
141
|
head_size = 24
|
|
142
|
-
return false if @buf.size
|
|
142
|
+
return false if @buf.size < head_size
|
|
143
143
|
|
|
144
144
|
magic, cmd, length, checksum = @buf.unpack("a4A12Va4")
|
|
145
145
|
payload = @buf[head_size...head_size+length]
|
data/lib/bitcoin/protocol/tx.rb
CHANGED
|
@@ -25,6 +25,9 @@ module Bitcoin
|
|
|
25
25
|
# lock time
|
|
26
26
|
attr_accessor :lock_time
|
|
27
27
|
|
|
28
|
+
# parsed / evaluated input scripts cached for later use
|
|
29
|
+
attr_reader :scripts
|
|
30
|
+
|
|
28
31
|
alias :inputs :in
|
|
29
32
|
alias :outputs :out
|
|
30
33
|
|
|
@@ -35,12 +38,12 @@ module Bitcoin
|
|
|
35
38
|
|
|
36
39
|
# return the tx hash in binary format
|
|
37
40
|
def binary_hash
|
|
38
|
-
[@hash].pack("H*").reverse
|
|
41
|
+
@binary_hash ||= [@hash].pack("H*").reverse
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
# create tx from raw binary +data+
|
|
42
45
|
def initialize(data=nil)
|
|
43
|
-
@ver, @lock_time, @in, @out = 1, 0, [], []
|
|
46
|
+
@ver, @lock_time, @in, @out, @scripts = 1, 0, [], [], []
|
|
44
47
|
parse_data_from_io(data) if data
|
|
45
48
|
end
|
|
46
49
|
|
|
@@ -102,24 +105,28 @@ module Bitcoin
|
|
|
102
105
|
|
|
103
106
|
# generate a signature hash for input +input_idx+.
|
|
104
107
|
# either pass the +outpoint_tx+ or the +script_pubkey+ directly.
|
|
105
|
-
def signature_hash_for_input(input_idx,
|
|
108
|
+
def signature_hash_for_input(input_idx, subscript, hash_type=nil)
|
|
106
109
|
# https://github.com/bitcoin/bitcoin/blob/e071a3f6c06f41068ad17134189a4ac3073ef76b/script.cpp#L834
|
|
107
110
|
# http://code.google.com/p/bitcoinj/source/browse/trunk/src/com/google/bitcoin/core/Script.java#318
|
|
108
111
|
# https://en.bitcoin.it/wiki/OP_CHECKSIG#How_it_works
|
|
109
112
|
# https://github.com/bitcoin/bitcoin/blob/c2e8c8acd8ae0c94c70b59f55169841ad195bb99/src/script.cpp#L1058
|
|
110
113
|
# https://en.bitcoin.it/wiki/OP_CHECKSIG
|
|
111
114
|
|
|
115
|
+
# Note: BitcoinQT checks if input_idx >= @in.size and returns 1 with an error message.
|
|
116
|
+
# But this check is never actually useful because BitcoinQT would crash
|
|
117
|
+
# right before VerifyScript if input index is out of bounds (inside CScriptCheck::operator()()).
|
|
118
|
+
# That's why we don't need to do such a check here.
|
|
119
|
+
#
|
|
120
|
+
# However, if you look at the case SIGHASH_TYPE[:single] below, we must
|
|
121
|
+
# return 1 because it's possible to have more inputs than outputs and BitcoinQT returns 1 as well.
|
|
112
122
|
return "\x01".ljust(32, "\x00") if input_idx >= @in.size # ERROR: SignatureHash() : input_idx=%d out of range
|
|
113
123
|
|
|
114
124
|
hash_type ||= SIGHASH_TYPE[:all]
|
|
115
125
|
|
|
116
126
|
pin = @in.map.with_index{|input,idx|
|
|
117
127
|
if idx == input_idx
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
script_pubkey = Bitcoin::Script.drop_signatures(script_pubkey, drop_sigs) if drop_sigs # array of signature to drop (slow)
|
|
121
|
-
#p Bitcoin::Script.new(script_pubkey).to_string
|
|
122
|
-
input.to_payload(script_pubkey)
|
|
128
|
+
subscript = subscript.out[ input.prev_out_index ].script if subscript.respond_to?(:out) # legacy api (outpoint_tx)
|
|
129
|
+
input.to_payload(subscript)
|
|
123
130
|
else
|
|
124
131
|
case (hash_type & 0x1f)
|
|
125
132
|
when SIGHASH_TYPE[:none]; input.to_payload("", "\x00\x00\x00\x00")
|
|
@@ -152,16 +159,22 @@ module Bitcoin
|
|
|
152
159
|
|
|
153
160
|
# verify input signature +in_idx+ against the corresponding
|
|
154
161
|
# output in +outpoint_tx+
|
|
155
|
-
|
|
162
|
+
# outpoint
|
|
163
|
+
def verify_input_signature(in_idx, outpoint_tx_or_script, block_timestamp=Time.now.to_i)
|
|
156
164
|
outpoint_idx = @in[in_idx].prev_out_index
|
|
157
165
|
script_sig = @in[in_idx].script_sig
|
|
158
|
-
|
|
159
|
-
script
|
|
166
|
+
|
|
167
|
+
# If given an entire previous transaction, take the script from it
|
|
168
|
+
script_pubkey = if outpoint_tx_or_script.respond_to?(:out)
|
|
169
|
+
outpoint_tx_or_script.out[outpoint_idx].pk_script
|
|
170
|
+
else
|
|
171
|
+
# Otherwise, it's already a script.
|
|
172
|
+
outpoint_tx_or_script
|
|
173
|
+
end
|
|
160
174
|
|
|
161
|
-
Bitcoin::Script.new(
|
|
162
|
-
|
|
163
|
-
hash = signature_hash_for_input(in_idx,
|
|
164
|
-
#hash = signature_hash_for_input(in_idx, nil, script_pubkey, hash_type, drop_sigs, script)
|
|
175
|
+
@scripts[in_idx] = Bitcoin::Script.new(script_sig, script_pubkey)
|
|
176
|
+
@scripts[in_idx].run(block_timestamp) do |pubkey,sig,hash_type,subscript|
|
|
177
|
+
hash = signature_hash_for_input(in_idx, subscript, hash_type)
|
|
165
178
|
Bitcoin.verify_signature( hash, sig, pubkey.unpack("H*")[0] )
|
|
166
179
|
end
|
|
167
180
|
end
|
|
@@ -170,7 +183,7 @@ module Bitcoin
|
|
|
170
183
|
def to_hash(options = {})
|
|
171
184
|
@hash ||= hash_from_payload(to_payload)
|
|
172
185
|
h = {
|
|
173
|
-
'hash' => @hash, 'ver' => @ver,
|
|
186
|
+
'hash' => @hash, 'ver' => @ver, # 'nid' => normalized_hash,
|
|
174
187
|
'vin_sz' => @in.size, 'vout_sz' => @out.size,
|
|
175
188
|
'lock_time' => @lock_time, 'size' => (@payload ||= to_payload).bytesize,
|
|
176
189
|
'in' => @in.map{|i| i.to_hash(options) },
|
|
@@ -215,13 +228,46 @@ module Bitcoin
|
|
|
215
228
|
# read json block from a file
|
|
216
229
|
def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
|
|
217
230
|
|
|
218
|
-
|
|
219
|
-
|
|
231
|
+
# Get a Bitcoin::Validation object to validate this block. It needs a +store+
|
|
232
|
+
# to validate against, a block to validate tx chains inside one block, and
|
|
233
|
+
# optionally takes the +block_validator+ as an optimization.
|
|
234
|
+
def validator(store, block = nil, block_validator = nil)
|
|
235
|
+
@validator ||= Bitcoin::Validation::Tx.new(self, store, block, block_validator)
|
|
220
236
|
end
|
|
221
237
|
|
|
222
238
|
def size
|
|
223
239
|
payload.bytesize
|
|
224
240
|
end
|
|
241
|
+
|
|
242
|
+
# Checks if transaction is final taking into account height and time
|
|
243
|
+
# of a block in which it is located (or about to be included if it's unconfirmed tx).
|
|
244
|
+
def is_final?(block_height, block_time)
|
|
245
|
+
# No time lock - tx is final.
|
|
246
|
+
return true if lock_time == 0
|
|
247
|
+
|
|
248
|
+
# Time based nLockTime implemented in 0.1.6
|
|
249
|
+
# If lock_time is below the magic threshold treat it as a block height.
|
|
250
|
+
# If lock_time is above the threshold, it's a unix timestamp.
|
|
251
|
+
return true if lock_time < (lock_time < Bitcoin::LOCKTIME_THRESHOLD ? block_height : block_time)
|
|
252
|
+
|
|
253
|
+
inputs.each{|input| return false if !input.is_final? }
|
|
254
|
+
|
|
255
|
+
return true
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def legacy_sigops_count
|
|
259
|
+
# Note: input scripts normally never have any opcodes since every input script
|
|
260
|
+
# can be statically reduced to a pushdata-only script.
|
|
261
|
+
# However, anyone is allowed to create a non-standard transaction with any opcodes in the inputs.
|
|
262
|
+
count = 0
|
|
263
|
+
self.in.each do |txin|
|
|
264
|
+
count += Bitcoin::Script.new(txin.script_sig).sigops_count_accurate(false)
|
|
265
|
+
end
|
|
266
|
+
self.out.each do |txout|
|
|
267
|
+
count += Bitcoin::Script.new(txout.pk_script).sigops_count_accurate(false)
|
|
268
|
+
end
|
|
269
|
+
count
|
|
270
|
+
end
|
|
225
271
|
|
|
226
272
|
DEFAULT_BLOCK_PRIORITY_SIZE = 27000
|
|
227
273
|
|
|
@@ -241,13 +287,24 @@ module Bitcoin
|
|
|
241
287
|
# multiple transactions instead of one big transaction to avoid fees.
|
|
242
288
|
# * If we are creating a transaction we allow transactions up to 1,000 bytes
|
|
243
289
|
# to be considered safe and assume they can likely make it into this section.
|
|
244
|
-
min_fee = 0 if tx_size < (mode == :block ?
|
|
290
|
+
min_fee = 0 if tx_size < (mode == :block ? Bitcoin.network[:free_tx_bytes] : DEFAULT_BLOCK_PRIORITY_SIZE - 1_000)
|
|
245
291
|
end
|
|
246
292
|
|
|
247
293
|
# This code can be removed after enough miners have upgraded to version 0.9.
|
|
248
294
|
# Until then, be safe when sending and require a fee if any output is less than CENT
|
|
249
295
|
if min_fee < base_fee && mode == :block
|
|
250
|
-
outputs.each
|
|
296
|
+
outputs.each do |output|
|
|
297
|
+
if output.value < Bitcoin.network[:dust]
|
|
298
|
+
# If per dust fee, then we add min fee for each output less than dust.
|
|
299
|
+
# Otherwise, we set to min fee if there is any output less than dust.
|
|
300
|
+
if Bitcoin.network[:per_dust_fee]
|
|
301
|
+
min_fee += base_fee
|
|
302
|
+
else
|
|
303
|
+
min_fee = base_fee
|
|
304
|
+
break
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|
|
251
308
|
end
|
|
252
309
|
|
|
253
310
|
min_fee = Bitcoin::network[:max_money] unless min_fee.between?(0, Bitcoin::network[:max_money])
|
|
@@ -258,6 +315,11 @@ module Bitcoin
|
|
|
258
315
|
inputs.size == 1 and inputs.first.coinbase?
|
|
259
316
|
end
|
|
260
317
|
|
|
318
|
+
def normalized_hash
|
|
319
|
+
signature_hash_for_input(-1, nil, SIGHASH_TYPE[:all]).unpack("H*")[0]
|
|
320
|
+
end
|
|
321
|
+
alias :nhash :normalized_hash
|
|
322
|
+
|
|
261
323
|
end
|
|
262
324
|
end
|
|
263
325
|
end
|