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.
- data/.gitignore +4 -1
- data/Gemfile +21 -0
- data/README.rdoc +85 -25
- data/Rakefile +7 -3
- data/bin/bitcoin_node +39 -42
- data/bin/bitcoin_shell +1 -0
- data/bin/bitcoin_wallet +129 -53
- data/bitcoin-ruby.gemspec +4 -7
- data/concept-examples/blockchain-pow.rb +1 -1
- data/doc/CONFIG.rdoc +5 -5
- data/doc/EXAMPLES.rdoc +9 -5
- data/doc/NAMECOIN.rdoc +34 -0
- data/doc/NODE.rdoc +147 -10
- data/examples/balance.rb +10 -4
- data/examples/bbe_verify_tx.rb +7 -2
- data/examples/forwarder.rb +73 -0
- data/examples/generate_tx.rb +34 -0
- data/examples/simple_network_monitor_and_util.rb +187 -0
- data/examples/verify_tx.rb +1 -1
- data/lib/bitcoin.rb +308 -18
- data/lib/bitcoin/builder.rb +62 -36
- data/lib/bitcoin/config.rb +2 -0
- data/lib/bitcoin/connection.rb +11 -8
- data/lib/bitcoin/electrum/mnemonic.rb +162 -0
- data/lib/bitcoin/ffi/openssl.rb +187 -21
- data/lib/bitcoin/gui/addr_view.rb +2 -0
- data/lib/bitcoin/gui/conn_view.rb +2 -0
- data/lib/bitcoin/gui/connection.rb +2 -0
- data/lib/bitcoin/gui/em_gtk.rb +2 -0
- data/lib/bitcoin/gui/gui.rb +2 -0
- data/lib/bitcoin/gui/helpers.rb +2 -0
- data/lib/bitcoin/gui/tree_view.rb +2 -0
- data/lib/bitcoin/gui/tx_view.rb +2 -0
- data/lib/bitcoin/key.rb +77 -11
- data/lib/bitcoin/litecoin.rb +81 -0
- data/lib/bitcoin/logger.rb +20 -1
- data/lib/bitcoin/namecoin.rb +279 -0
- data/lib/bitcoin/network/command_client.rb +7 -6
- data/lib/bitcoin/network/command_handler.rb +229 -43
- data/lib/bitcoin/network/connection_handler.rb +182 -70
- data/lib/bitcoin/network/node.rb +231 -106
- data/lib/bitcoin/protocol.rb +44 -23
- data/lib/bitcoin/protocol/address.rb +5 -3
- data/lib/bitcoin/protocol/alert.rb +3 -4
- data/lib/bitcoin/protocol/aux_pow.rb +123 -0
- data/lib/bitcoin/protocol/block.rb +98 -18
- data/lib/bitcoin/protocol/handler.rb +6 -5
- data/lib/bitcoin/protocol/parser.rb +44 -19
- data/lib/bitcoin/protocol/tx.rb +105 -52
- data/lib/bitcoin/protocol/txin.rb +39 -19
- data/lib/bitcoin/protocol/txout.rb +28 -13
- data/lib/bitcoin/protocol/version.rb +16 -7
- data/lib/bitcoin/script.rb +579 -122
- data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
- data/lib/bitcoin/storage/models.rb +20 -7
- data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
- data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
- data/lib/bitcoin/storage/storage.rb +233 -28
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
- data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
- data/lib/bitcoin/validation.rb +369 -0
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/coinselector.rb +3 -0
- data/lib/bitcoin/wallet/keygenerator.rb +3 -1
- data/lib/bitcoin/wallet/keystore.rb +6 -2
- data/lib/bitcoin/wallet/txdp.rb +6 -4
- data/lib/bitcoin/wallet/wallet.rb +54 -16
- data/spec/bitcoin/bitcoin_spec.rb +48 -3
- data/spec/bitcoin/builder_spec.rb +40 -17
- data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
- data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
- data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
- data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
- data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
- data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
- data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
- data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
- data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
- data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
- data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
- data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
- data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
- data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
- data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
- data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
- data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
- data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
- data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
- data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
- data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
- data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
- data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
- data/spec/bitcoin/key_spec.rb +128 -3
- data/spec/bitcoin/namecoin_spec.rb +182 -0
- data/spec/bitcoin/network_spec.rb +5 -3
- data/spec/bitcoin/node/command_api_spec.rb +376 -0
- data/spec/bitcoin/protocol/addr_spec.rb +2 -0
- data/spec/bitcoin/protocol/alert_spec.rb +2 -0
- data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
- data/spec/bitcoin/protocol/block_spec.rb +134 -39
- data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
- data/spec/bitcoin/protocol/inv_spec.rb +10 -8
- data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
- data/spec/bitcoin/protocol/ping_spec.rb +2 -0
- data/spec/bitcoin/protocol/tx_spec.rb +83 -17
- data/spec/bitcoin/protocol/version_spec.rb +7 -5
- data/spec/bitcoin/script/opcodes_spec.rb +412 -133
- data/spec/bitcoin/script/script_spec.rb +112 -13
- data/spec/bitcoin/spec_helper.rb +68 -0
- data/spec/bitcoin/storage/reorg_spec.rb +199 -0
- data/spec/bitcoin/storage/storage_spec.rb +337 -0
- data/spec/bitcoin/storage/validation_spec.rb +261 -0
- data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
- data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
- data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
- data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
- data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
- metadata +105 -51
- data/lib/bitcoin/storage/sequel.rb +0 -335
- data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
- data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
- data/spec/bitcoin/reorg_spec.rb +0 -129
- data/spec/bitcoin/storage_spec.rb +0 -229
data/lib/bitcoin/protocol.rb
CHANGED
@@ -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("
|
90
|
+
length = [payload.bytesize].pack("V")
|
67
91
|
checksum = Digest::SHA256.digest(Digest::SHA256.digest(payload))[0...4]
|
68
|
-
|
69
|
-
|
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
|
102
|
-
t = [ TypeLookup[type] ].pack("
|
103
|
-
pkt("getdata",
|
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
|
108
|
-
t = [ TypeLookup[type] ].pack("
|
109
|
-
pkt("inv",
|
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
|
-
|
140
|
+
[version].pack("V"),
|
117
141
|
pack_var_int(locator_hashes.size),
|
118
|
-
locator_hashes.map{|l|
|
119
|
-
|
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("
|
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("
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
52
|
-
|
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.
|
89
|
+
payload = t.parse_data_from_io(buf)
|
55
90
|
@tx << t
|
56
91
|
}
|
57
92
|
|
58
93
|
@payload = to_payload
|
59
|
-
|
94
|
+
buf
|
60
95
|
end
|
61
96
|
|
62
97
|
# recalculate the block hash
|
63
98
|
def recalc_block_hash
|
64
|
-
@hash = Bitcoin.block_hash(
|
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,
|
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("
|
79
|
-
|
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' =>
|
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|
|
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
|