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
@@ -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