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