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
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  require 'socket'
2
4
  require 'digest/sha2'
3
5
  require 'json'
@@ -5,6 +7,12 @@ require 'json'
5
7
  module Bitcoin
6
8
  module Protocol
7
9
 
10
+ # bitcoin/src/main.h
11
+ MAX_INV_SZ = 50000
12
+
13
+ # BIP 0031, pong message, is enabled for all versions AFTER this one
14
+ BIP0031_VERSION = 60000
15
+
8
16
  autoload :TxIn, 'bitcoin/protocol/txin'
9
17
  autoload :TxOut, 'bitcoin/protocol/txout'
10
18
  autoload :Tx, 'bitcoin/protocol/tx'
@@ -12,15 +20,15 @@ module Bitcoin
12
20
  autoload :Addr, 'bitcoin/protocol/address'
13
21
  autoload :Alert, 'bitcoin/protocol/alert'
14
22
  autoload :Version, 'bitcoin/protocol/version'
23
+ autoload :AuxPow, 'bitcoin/protocol/aux_pow'
15
24
 
16
25
  autoload :Handler, 'bitcoin/protocol/handler'
17
26
  autoload :Parser, 'bitcoin/protocol/parser'
18
27
 
19
- VERSION = 60001
20
-
21
- DNS_Seed = [ "bitseed.xf2.org", "bitseed.bitcoin.org.uk" ]
22
28
  Uniq = rand(0xffffffffffffffff)
23
29
 
30
+ # var_int refers to https://en.bitcoin.it/wiki/Protocol_specification#Variable_length_integer and is what Satoshi called "CompactSize"
31
+ # BitcoinQT has later added even more compact format called CVarInt to use in its local block storage. CVarInt is not implemented here.
24
32
  def self.unpack_var_int(payload)
25
33
  case payload.unpack("C")[0] # TODO add test cases
26
34
  when 0xfd; payload.unpack("xva*")
@@ -30,6 +38,16 @@ module Bitcoin
30
38
  end
31
39
  end
32
40
 
41
+ def self.unpack_var_int_from_io(io)
42
+ uchar = io.read(1).unpack("C")[0]
43
+ case uchar
44
+ when 0xfd; io.read(2).unpack("v")[0]
45
+ when 0xfe; io.read(4).unpack("V")[0]
46
+ when 0xff; io.read(8).unpack("Q")[0]
47
+ else; uchar
48
+ end
49
+ end
50
+
33
51
  def self.pack_var_int(i)
34
52
  if i < 0xfd; [ i].pack("C")
35
53
  elsif i <= 0xffff; [0xfd, i].pack("Cv")
@@ -44,6 +62,11 @@ module Bitcoin
44
62
  size > 0 ? (string, payload = payload.unpack("a#{size}a*")) : [nil, payload]
45
63
  end
46
64
 
65
+ def self.unpack_var_string_from_io(buf)
66
+ size = unpack_var_int_from_io(buf)
67
+ size > 0 ? buf.read(size) : nil
68
+ end
69
+
47
70
  def self.pack_var_string(payload)
48
71
  pack_var_int(payload.bytesize) + payload
49
72
  end
@@ -60,13 +83,14 @@ module Bitcoin
60
83
  [(0...size).map{ i, payload = unpack_var_int(payload); i }, payload]
61
84
  end
62
85
 
86
+ BINARY = Encoding.find('ASCII-8BIT')
63
87
 
64
88
  def self.pkt(command, payload)
65
89
  cmd = command.ljust(12, "\x00")[0...12]
66
- length = [payload.bytesize].pack("I")
90
+ length = [payload.bytesize].pack("V")
67
91
  checksum = Digest::SHA256.digest(Digest::SHA256.digest(payload))[0...4]
68
-
69
- [Bitcoin.network[:magic_head], cmd, length, checksum, payload].join
92
+ pkt = "".force_encoding(BINARY)
93
+ pkt << Bitcoin.network[:magic_head].force_encoding(BINARY) << cmd.force_encoding(BINARY) << length << checksum << payload.force_encoding(BINARY)
70
94
  end
71
95
 
72
96
  def self.version_pkt(from_id, from=nil, to=nil, last_block=nil, time=nil, user_agent=nil, version=nil)
@@ -98,39 +122,36 @@ module Bitcoin
98
122
  TypeLookup = Hash[:tx, 1, :block, 2, nil, 0]
99
123
 
100
124
  def self.getdata_pkt(type, hashes)
101
- return if hashes.size >= 256
102
- t = [ TypeLookup[type] ].pack("I")
103
- pkt("getdata", [hashes.size].pack("C") + hashes.map{|hash| t + hash[0..32].reverse }.join)
125
+ return if hashes.size > MAX_INV_SZ
126
+ t = [ TypeLookup[type] ].pack("V")
127
+ pkt("getdata", pack_var_int(hashes.size) + hashes.map{|hash| t + hash[0..32].reverse }.join)
104
128
  end
105
129
 
106
130
  def self.inv_pkt(type, hashes)
107
- return if hashes.size >= 256
108
- t = [ TypeLookup[type] ].pack("I")
109
- pkt("inv", [hashes.size].pack("C") + hashes.map{|hash| t + hash[0..32].reverse }.join)
131
+ return if hashes.size > MAX_INV_SZ
132
+ t = [ TypeLookup[type] ].pack("V")
133
+ pkt("inv", pack_var_int(hashes.size) + hashes.map{|hash| t + hash[0..32].reverse }.join)
110
134
  end
111
135
 
112
136
  DEFAULT_STOP_HASH = "00"*32
113
137
 
114
- def self.locator_payload(locator_hashes, stop_hash)
138
+ def self.locator_payload(version, locator_hashes, stop_hash)
115
139
  payload = [
116
- Bitcoin.network[:magic_head],
140
+ [version].pack("V"),
117
141
  pack_var_int(locator_hashes.size),
118
- locator_hashes.map{|l| htb(l).reverse }.join,
119
- htb(stop_hash).reverse
142
+ locator_hashes.map{|l| l.htb_reverse }.join,
143
+ stop_hash.htb_reverse
120
144
  ].join
121
145
  end
122
146
 
123
- def self.getblocks_pkt(locator_hashes, stop_hash=DEFAULT_STOP_HASH)
124
- pkt "getblocks", locator_payload(locator_hashes, stop_hash)
147
+ def self.getblocks_pkt(version, locator_hashes, stop_hash=DEFAULT_STOP_HASH)
148
+ pkt "getblocks", locator_payload(version, locator_hashes, stop_hash)
125
149
  end
126
150
 
127
- def self.getheaders_pkt(locator_hashes, stop_hash=DEFAULT_STOP_HASH)
128
- pkt "getheaders", locator_payload(locator_hashes, stop_hash)
151
+ def self.getheaders_pkt(version, locator_hashes, stop_hash=DEFAULT_STOP_HASH)
152
+ pkt "getheaders", locator_payload(version, locator_hashes, stop_hash)
129
153
  end
130
154
 
131
- def self.hth(h); h.unpack("H*")[0]; end
132
- def self.htb(h); [h].pack("H*"); end
133
-
134
155
  def self.read_binary_file(path)
135
156
  File.open(path, 'rb'){|f| f.read }
136
157
  end
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin
2
4
  module Protocol
3
5
 
@@ -15,7 +17,7 @@ module Bitcoin
15
17
  # create addr from raw binary +data+
16
18
  def initialize(data = nil)
17
19
  if data
18
- self[:time], self[:service], self[:ip], self[:port] = data.unpack("IQx12a4n")
20
+ self[:time], self[:service], self[:ip], self[:port] = data.unpack("VQx12a4n")
19
21
  self[:ip] = ip.unpack("C*").join(".")
20
22
  else
21
23
  self[:time], self[:service] = Time.now.to_i, 1
@@ -30,7 +32,7 @@ module Bitcoin
30
32
 
31
33
  def to_payload
32
34
  ip = self[:ip].split(".").map(&:to_i)
33
- [ time, service, ("\x00"*10)+"\xff\xff", *ip, port ].pack("IQa12C4n")
35
+ [ time, service, ("\x00"*10)+"\xff\xff", *ip, port ].pack("VQa12C4n")
34
36
  end
35
37
 
36
38
  def string
@@ -38,7 +40,7 @@ module Bitcoin
38
40
  end
39
41
 
40
42
  def self.pkt(*addrs)
41
- addrs = addrs.select{|i| i.is_a?(Bitcoin::Protocol::Addr) }
43
+ addrs = addrs.select{|i| i.is_a?(Bitcoin::Protocol::Addr) && i.ip =~ /^\d+\.\d+\.\d+\.\d+$/ }
42
44
  length = Bitcoin::Protocol.pack_var_int(addrs.size)
43
45
  Bitcoin::Protocol.pkt("addr", length + addrs.map(&:to_payload).join)
44
46
  end
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin
2
4
  module Protocol
3
5
 
@@ -11,13 +13,10 @@ module Protocol
11
13
  super(*values)
12
14
  end
13
15
 
14
-
15
- Valid_Keys = [ "04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284" ]
16
-
17
16
  def valid_signature?
18
17
  return false unless @payload && @signature
19
18
  hash = Digest::SHA256.digest(Digest::SHA256.digest(@payload))
20
- Valid_Keys.any?{|public_key| Bitcoin.verify_signature(hash, @signature, public_key) }
19
+ Bitcoin.network[:alert_pubkeys].any?{|public_key| Bitcoin.verify_signature(hash, @signature, public_key) }
21
20
  end
22
21
 
23
22
 
@@ -0,0 +1,123 @@
1
+ # encoding: ascii-8bit
2
+
3
+ module Bitcoin
4
+ module Protocol
5
+
6
+ # Auxiliary Proof-of-Work for merge-mined blockchains
7
+ class AuxPow
8
+
9
+ # Coinbase transaction linking the aux to its parent block
10
+ attr_accessor :tx
11
+
12
+ # Hash of the block header
13
+ attr_accessor :block_hash
14
+
15
+ # Merkle branches to bring the transaction to the block's merkle root
16
+ attr_accessor :branch
17
+
18
+ # Index of this transaction in the merkle tree
19
+ attr_accessor :mrkl_index
20
+
21
+ # Merkle branches linking this aux chains to the aux root
22
+ attr_accessor :aux_branch
23
+
24
+ # Index of "this" block chain in the aux chain list
25
+ attr_accessor :aux_index
26
+
27
+ # Parent block header
28
+ attr_accessor :parent_block
29
+
30
+ def initialize(data)
31
+ parse_data (data) if data
32
+ end
33
+
34
+ def parse_data(data)
35
+ @tx = P::Tx.new(nil)
36
+ payload = @tx.parse_data(data)
37
+ @block_hash, payload = payload.unpack("a32a*")
38
+
39
+ branch_count, payload = P.unpack_var_int(payload)
40
+ @branch = []
41
+ branch_count.times {
42
+ b, payload = payload.unpack("a32a*")
43
+ @branch << b
44
+ }
45
+ @mrkl_index, payload = payload.unpack("Ia*")
46
+
47
+ @aux_branch = []
48
+ aux_branch_count, payload = P.unpack_var_int(payload)
49
+ aux_branch_count.times {
50
+ b, payload = payload.unpack("a32a*")
51
+ @aux_branch << b
52
+ }
53
+
54
+ @aux_index, payload = payload.unpack("Ia*")
55
+ block, payload = payload.unpack("a80a*")
56
+ @parent_block = P::Block.new(block)
57
+
58
+ payload
59
+ end
60
+
61
+ def parse_data_from_io(data)
62
+ @tx = P::Tx.new(nil)
63
+ @tx.parse_data_from_io(data)
64
+
65
+ @block_hash = data.read(32)
66
+ branch_count = P.unpack_var_int_from_io(data)
67
+ @branch = []
68
+ branch_count.times{ @branch << data.read(32) }
69
+ @mrkl_index = data.read(4).unpack("I")[0]
70
+
71
+ @aux_branch = []
72
+ aux_branch_count = P.unpack_var_int_from_io(data)
73
+ aux_branch_count.times{ @aux_branch << data.read(32) }
74
+
75
+ @aux_index = data.read(4).unpack("I")[0]
76
+ block = data.read(80)
77
+ @parent_block = P::Block.new(block)
78
+
79
+ data
80
+ end
81
+
82
+
83
+ def to_payload
84
+ payload = @tx.to_payload
85
+ payload << @block_hash
86
+ payload << P.pack_var_int(@branch.count)
87
+ payload << @branch.join
88
+ payload << [@mrkl_index].pack("I")
89
+ payload << P.pack_var_int(@aux_branch.count)
90
+ payload << @aux_branch.join
91
+ payload << [@aux_index].pack("I")
92
+ payload << @parent_block.to_payload
93
+ payload
94
+ end
95
+
96
+ def self.from_hash h
97
+ aux_pow = new(nil)
98
+ aux_pow.instance_eval do
99
+ @tx = P::Tx.from_hash(h['tx'])
100
+ @block_hash = h['block_hash'].htb
101
+ @branch = h['branch'].map(&:htb)
102
+ @mrkl_index = h['mrkl_index']
103
+ @aux_branch = h['aux_branch'].map(&:htb)
104
+ @aux_index = h['aux_index']
105
+ @parent_block = P::Block.from_hash(h['parent_block'])
106
+ end
107
+ aux_pow
108
+ end
109
+
110
+ def to_hash
111
+ { 'tx' => @tx.to_hash,
112
+ 'block_hash' => @block_hash.hth,
113
+ 'branch' => @branch.map(&:hth),
114
+ 'mrkl_index' => @mrkl_index,
115
+ 'aux_branch' => @aux_branch.map(&:hth),
116
+ 'aux_index' => @aux_index,
117
+ 'parent_block' => @parent_block.to_hash }
118
+ end
119
+
120
+ end
121
+
122
+ end
123
+ end
@@ -1,8 +1,15 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin
2
4
  module Protocol
3
5
 
4
6
  class Block
5
7
 
8
+ BLOCK_VERSION_DEFAULT = (1 << 0)
9
+ BLOCK_VERSION_AUXPOW = (1 << 8)
10
+ BLOCK_VERSION_CHAIN_START = (1 << 16)
11
+ BLOCK_VERSION_CHAIN_END = (1 << 30)
12
+
6
13
  # block hash
7
14
  attr_accessor :hash
8
15
 
@@ -30,6 +37,9 @@ module Bitcoin
30
37
  # raw protocol payload
31
38
  attr_accessor :payload
32
39
 
40
+ # AuxPow linking the block to a merge-mined chain
41
+ attr_accessor :aux_pow
42
+
33
43
  alias :transactions :tx
34
44
 
35
45
  # compare to another block
@@ -37,58 +47,95 @@ module Bitcoin
37
47
  @hash == other.hash
38
48
  end
39
49
 
50
+ def binary_hash
51
+ [@hash].pack("H*")
52
+ end
53
+
54
+ def prev_block_hex
55
+ @prev_block_hex ||= @prev_block.reverse.unpack("H*")[0]
56
+ end
57
+
40
58
  # create block from raw binary +data+
41
59
  def initialize(data)
42
60
  @tx = []
43
- parse_data(data) if data
61
+ parse_data_from_io(data) if data
44
62
  end
45
63
 
46
64
  # parse raw binary data
47
65
  def parse_data(data)
48
- @ver, @prev_block, @mrkl_root, @time, @bits, @nonce, payload = data.unpack("Ia32a32IIIa*")
66
+ buf = parse_data_from_io(data)
67
+ buf.eof? ? true : buf.read
68
+ end
69
+
70
+ # parse raw binary data
71
+ def parse_data_from_io(buf, header_only=false)
72
+ buf = buf.is_a?(String) ? StringIO.new(buf) : buf
73
+ @ver, @prev_block, @mrkl_root, @time, @bits, @nonce = buf.read(80).unpack("Va32a32VVV")
49
74
  recalc_block_hash
50
75
 
51
- tx_size, payload = Protocol.unpack_var_int(payload)
52
- (0...tx_size).each{ break if payload == true
76
+ if (@ver & BLOCK_VERSION_AUXPOW) > 0
77
+ @aux_pow = AuxPow.new(nil)
78
+ @aux_pow.parse_data_from_io(buf)
79
+ end
80
+
81
+ return buf if buf.eof?
82
+
83
+ tx_size = Protocol.unpack_var_int_from_io(buf)
84
+ @tx_count = tx_size
85
+ return buf if header_only
86
+
87
+ tx_size.times{ break if payload == true
53
88
  t = Tx.new(nil)
54
- payload = t.parse_data(payload)
89
+ payload = t.parse_data_from_io(buf)
55
90
  @tx << t
56
91
  }
57
92
 
58
93
  @payload = to_payload
59
- payload
94
+ buf
60
95
  end
61
96
 
62
97
  # recalculate the block hash
63
98
  def recalc_block_hash
64
- @hash = Bitcoin.block_hash(hth(@prev_block), hth(@mrkl_root), @time, @bits, @nonce, @ver)
99
+ @hash = Bitcoin.block_hash(@prev_block.reverse_hth, @mrkl_root.reverse_hth, @time, @bits, @nonce, @ver)
100
+ end
101
+
102
+ def recalc_mrkl_root
103
+ @mrkl_root = Bitcoin.hash_mrkl_tree( @tx.map(&:hash) ).last.htb_reverse
104
+ end
105
+
106
+ # verify mrkl tree
107
+ def verify_mrkl_root
108
+ @mrkl_root.reverse_hth == Bitcoin.hash_mrkl_tree( @tx.map(&:hash) ).last
65
109
  end
66
110
 
67
111
  # get the block header info
68
112
  # [<version>, <prev_block>, <merkle_root>, <time>, <bits>, <nonce>, <txcount>, <size>]
69
113
  def header_info
70
- [@ver, hth(@prev_block), hth(@mrkl_root), Time.at(@time), @bits, @nonce, @tx.size, @payload.size]
114
+ [@ver, @prev_block.reverse_hth, @mrkl_root.reverse_hth, Time.at(@time), @bits, @nonce, @tx.size, @payload.size]
71
115
  end
72
116
 
73
- def hth(h); h.reverse.unpack("H*")[0]; end
74
- def htb(s); [s].pack('H*').reverse; end
75
-
76
117
  # convert to raw binary format
77
118
  def to_payload
78
- head = [@ver, @prev_block, @mrkl_root, @time, @bits, @nonce].pack("Ia32a32III")
79
- [head, Protocol.pack_var_int(@tx.size), @tx.map(&:to_payload).join].join
119
+ head = [@ver, @prev_block, @mrkl_root, @time, @bits, @nonce].pack("Va32a32VVV")
120
+ head << @aux_pow.to_payload if @aux_pow
121
+ return head if @tx.size == 0
122
+ head << Protocol.pack_var_int(@tx.size)
123
+ @tx.each{|tx| head << tx.to_payload }
124
+ head
80
125
  end
81
126
 
82
127
  # convert to ruby hash (see also #from_hash)
83
128
  def to_hash
84
- {
129
+ h = {
85
130
  'hash' => @hash, 'ver' => @ver,
86
- 'prev_block' => hth(@prev_block), 'mrkl_root' => hth(@mrkl_root),
131
+ 'prev_block' => @prev_block.reverse_hth, 'mrkl_root' => @mrkl_root.reverse_hth,
87
132
  'time' => @time, 'bits' => @bits, 'nonce' => @nonce,
88
133
  'n_tx' => @tx.size, 'size' => (@payload||to_payload).bytesize,
89
134
  'tx' => @tx.map{|i| i.to_hash },
90
135
  'mrkl_tree' => Bitcoin.hash_mrkl_tree( @tx.map{|i| i.hash } )
91
136
  }
137
+ h['aux_pow'] = @aux_pow.to_hash if @aux_pow
138
+ h
92
139
  end
93
140
 
94
141
  def hextarget
@@ -103,6 +150,22 @@ module Bitcoin
103
150
  Bitcoin.block_difficulty(@bits)
104
151
  end
105
152
 
153
+ # introduced in block version 2 by BIP_0034
154
+ # blockchain height as seen by the block itself.
155
+ # do not trust this value, instead verify with chain storage.
156
+ def bip34_block_height(height=nil)
157
+ return nil unless @ver >= 2
158
+ if height # generate height binary
159
+ buf = [height].pack("V").gsub(/\x00+$/,"")
160
+ [buf.bytesize, buf].pack("Ca*")
161
+ else
162
+ coinbase = @tx.first.inputs.first.script_sig
163
+ coinbase[1..coinbase[0].ord].ljust(4, "\x00").unpack("V").first
164
+ end
165
+ rescue
166
+ nil
167
+ end
168
+
106
169
  # convert to json representation as seen in the block explorer.
107
170
  # (see also #from_json)
108
171
  def to_json(options = {:space => ''}, *a)
@@ -116,13 +179,19 @@ module Bitcoin
116
179
  end
117
180
 
118
181
  # parse ruby hash (see also #to_hash)
119
- def self.from_hash(h)
182
+ def self.from_hash(h, do_raise=true)
120
183
  blk = new(nil)
121
184
  blk.instance_eval{
122
185
  @ver, @time, @bits, @nonce = h.values_at('ver', 'time', 'bits', 'nonce')
123
- @prev_block, @mrkl_root = h.values_at('prev_block', 'mrkl_root').map{|i| htb(i) }
124
- recalc_block_hash
186
+ @prev_block, @mrkl_root = h.values_at('prev_block', 'mrkl_root').map{|i| i.htb_reverse }
187
+ unless h['hash'] == recalc_block_hash
188
+ raise "Block hash mismatch! Claimed: #{h['hash']}, Actual: #{@hash}" if do_raise
189
+ end
190
+ @aux_pow = AuxPow.from_hash(h['aux_pow']) if h['aux_pow']
125
191
  h['tx'].each{|tx| @tx << Tx.from_hash(tx) }
192
+ if h['tx'].any? && !Bitcoin.freicoin?
193
+ (raise "Block merkle root mismatch! Block: #{h['hash']}" unless verify_mrkl_root) if do_raise
194
+ end
126
195
  }
127
196
  blk
128
197
  end
@@ -148,6 +217,17 @@ module Bitcoin
148
217
 
149
218
  # read json block from a file
150
219
  def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
220
+
221
+ def validator(store, prev_block = nil)
222
+ @validator ||= Bitcoin::Validation::Block.new(self, store, prev_block)
223
+ end
224
+
225
+ # get the (statistical) amount of work that was needed to generate this block.
226
+ def block_work
227
+ target = Bitcoin.decode_compact_bits(@bits)
228
+ (2**256) / (target.to_i(16) + 1)
229
+ end
230
+
151
231
  end
152
232
 
153
233
  end