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.
- 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
|
@@ -1,21 +1,23 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
1
3
|
module Bitcoin
|
|
2
4
|
module Protocol
|
|
3
5
|
|
|
4
6
|
class Handler
|
|
5
7
|
def on_inv_transaction(hash)
|
|
6
|
-
p ['inv transaction', hth
|
|
8
|
+
p ['inv transaction', hash.hth]
|
|
7
9
|
end
|
|
8
10
|
|
|
9
11
|
def on_inv_block(hash)
|
|
10
|
-
p ['inv block', hth
|
|
12
|
+
p ['inv block', hash.hth]
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def on_get_transaction(hash)
|
|
14
|
-
p ['get transaction', hth
|
|
16
|
+
p ['get transaction', hash.hth]
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def on_get_block(hash)
|
|
18
|
-
p ['get block', hth
|
|
20
|
+
p ['get block', hash.hth]
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
def on_addr(addr)
|
|
@@ -31,7 +33,6 @@ module Bitcoin
|
|
|
31
33
|
puts block.to_json
|
|
32
34
|
end
|
|
33
35
|
|
|
34
|
-
def hth(h); h.unpack("H*")[0]; end
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
1
3
|
module Bitcoin
|
|
2
4
|
module Protocol
|
|
3
5
|
|
|
@@ -37,8 +39,6 @@ module Bitcoin
|
|
|
37
39
|
}
|
|
38
40
|
end
|
|
39
41
|
|
|
40
|
-
def hth(h); h.unpack("H*")[0]; end
|
|
41
|
-
|
|
42
42
|
def parse_addr(payload)
|
|
43
43
|
count, payload = Protocol.unpack_var_int(payload)
|
|
44
44
|
payload.each_byte.each_slice(30){|i|
|
|
@@ -52,18 +52,19 @@ module Bitcoin
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def parse_headers(payload)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
return unless @h.respond_to?(:on_headers)
|
|
56
|
+
buf = StringIO.new(payload)
|
|
57
|
+
count = Protocol.unpack_var_int_from_io(buf)
|
|
58
|
+
headers = count.times.map{ b = Block.new; b.parse_data_from_io(buf, header_only=true); b }
|
|
58
59
|
@h.on_headers(headers)
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
def parse_getblocks(payload)
|
|
62
|
-
version, payload = payload.unpack('
|
|
63
|
+
version, payload = payload.unpack('Va*')
|
|
63
64
|
count, payload = Protocol.unpack_var_int(payload)
|
|
64
65
|
buf, payload = payload.unpack("a#{count*32}a*")
|
|
65
|
-
hashes = buf.each_byte.each_slice(32).map{|i| hash =
|
|
66
|
-
stop_hash =
|
|
66
|
+
hashes = buf.each_byte.each_slice(32).map{|i| hash = i.reverse.pack("C32").hth }
|
|
67
|
+
stop_hash = payload[0..32].reverse_hth
|
|
67
68
|
[version, hashes, stop_hash]
|
|
68
69
|
end
|
|
69
70
|
|
|
@@ -75,21 +76,24 @@ module Bitcoin
|
|
|
75
76
|
when 'inv'; parse_inv(payload, :put)
|
|
76
77
|
when 'getdata'; parse_inv(payload, :get)
|
|
77
78
|
when 'addr'; parse_addr(payload)
|
|
78
|
-
when '
|
|
79
|
+
when 'getaddr'; @h.on_getaddr if @h.respond_to?(:on_getaddr)
|
|
80
|
+
when 'verack'; @h.respond_to?(:on_verack) ? @h.on_verack : (@h.respond_to?(:on_handshake_complete) ? @h.on_handshake_complete : nil)
|
|
79
81
|
when 'version'; parse_version(payload)
|
|
80
82
|
when 'alert'; parse_alert(payload)
|
|
81
83
|
when 'ping'; @h.on_ping(payload.unpack("Q")[0])
|
|
82
84
|
when 'pong'; @h.on_pong(payload.unpack("Q")[0])
|
|
83
|
-
when 'getblocks'; @h.on_getblocks(*parse_getblocks(payload))
|
|
84
|
-
when 'getheaders'; @h.on_getheaders(*parse_getblocks(payload))
|
|
85
|
+
when 'getblocks'; @h.on_getblocks(*parse_getblocks(payload)) if @h.respond_to?(:on_getblocks)
|
|
86
|
+
when 'getheaders'; @h.on_getheaders(*parse_getblocks(payload)) if @h.respond_to?(:on_getheaders)
|
|
87
|
+
when 'mempool'; handle_mempool_request(payload)
|
|
88
|
+
when 'notfound'; handle_notfound_reply(payload)
|
|
85
89
|
else
|
|
86
|
-
p ['
|
|
90
|
+
p ['unknown-packet', command, payload]
|
|
87
91
|
end
|
|
88
92
|
end
|
|
89
93
|
|
|
90
94
|
def parse_version(payload)
|
|
91
|
-
version = Bitcoin::Protocol::Version.parse(payload)
|
|
92
|
-
@h.on_version(version)
|
|
95
|
+
@version = Bitcoin::Protocol::Version.parse(payload)
|
|
96
|
+
@h.on_version(@version)
|
|
93
97
|
end
|
|
94
98
|
|
|
95
99
|
def parse_alert(payload)
|
|
@@ -97,6 +101,27 @@ module Bitcoin
|
|
|
97
101
|
@h.on_alert Bitcoin::Protocol::Alert.parse(payload)
|
|
98
102
|
end
|
|
99
103
|
|
|
104
|
+
# https://en.bitcoin.it/wiki/BIP_0035
|
|
105
|
+
def handle_mempool_request(payload)
|
|
106
|
+
return unless @version[:version] >= 60002 # Protocol version >= 60002
|
|
107
|
+
return unless (@version[:services] & Bitcoin::Protocol::Version::NODE_NETWORK) == 1 # NODE_NETWORK bit set in Services
|
|
108
|
+
@h.on_mempool if @h.respond_to?(:on_mempool)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def handle_notfound_reply(payload)
|
|
112
|
+
return unless @h.respond_to?(:on_notfound)
|
|
113
|
+
count, payload = Protocol.unpack_var_int(payload)
|
|
114
|
+
payload.each_byte.each_slice(36){|i|
|
|
115
|
+
hash = i[4..-1].reverse.pack("C32")
|
|
116
|
+
case i[0]
|
|
117
|
+
when 1; @h.on_notfound(:tx, hash)
|
|
118
|
+
when 2; @h.on_notfound(:block, hash)
|
|
119
|
+
else
|
|
120
|
+
p ['handle_notfound_reply error', i, hash]
|
|
121
|
+
end
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
|
|
100
125
|
def parse(buf)
|
|
101
126
|
@buf += buf
|
|
102
127
|
while parse_buffer; end
|
|
@@ -108,20 +133,20 @@ module Bitcoin
|
|
|
108
133
|
head_size = 24
|
|
109
134
|
return false if @buf.size <= head_size
|
|
110
135
|
|
|
111
|
-
magic, cmd, length, checksum = @buf.unpack("
|
|
136
|
+
magic, cmd, length, checksum = @buf.unpack("a4A12Va4")
|
|
112
137
|
payload = @buf[head_size...head_size+length]
|
|
113
138
|
|
|
114
139
|
unless magic == head_magic
|
|
115
|
-
|
|
140
|
+
handle_stream_error(:close, "head_magic not found")
|
|
116
141
|
@buf = ''
|
|
117
142
|
else
|
|
118
143
|
|
|
119
144
|
if Digest::SHA256.digest(Digest::SHA256.digest( payload ))[0...4] != checksum
|
|
120
145
|
if (length < 50000) && (payload.size < length)
|
|
121
146
|
size_info = [payload.size, length].join('/')
|
|
122
|
-
|
|
147
|
+
handle_stream_error(:debug, "chunked packet stream (#{size_info})")
|
|
123
148
|
else
|
|
124
|
-
|
|
149
|
+
handle_stream_error(:close, "checksum mismatch")
|
|
125
150
|
end
|
|
126
151
|
return
|
|
127
152
|
end
|
|
@@ -134,7 +159,7 @@ module Bitcoin
|
|
|
134
159
|
@buf[0] != nil
|
|
135
160
|
end
|
|
136
161
|
|
|
137
|
-
def
|
|
162
|
+
def handle_stream_error(type, msg)
|
|
138
163
|
case type
|
|
139
164
|
when :close
|
|
140
165
|
log.debug {"closing packet stream (#{msg})"}
|
data/lib/bitcoin/protocol/tx.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
1
3
|
require 'bitcoin/script'
|
|
2
4
|
|
|
3
5
|
module Bitcoin
|
|
@@ -39,12 +41,12 @@ module Bitcoin
|
|
|
39
41
|
# create tx from raw binary +data+
|
|
40
42
|
def initialize(data=nil)
|
|
41
43
|
@ver, @lock_time, @in, @out = 1, 0, [], []
|
|
42
|
-
|
|
44
|
+
parse_data_from_io(data) if data
|
|
43
45
|
end
|
|
44
46
|
|
|
45
47
|
# generate the tx hash for given +payload+ in hex format
|
|
46
48
|
def hash_from_payload(payload)
|
|
47
|
-
Digest::SHA256.digest(Digest::SHA256.digest( payload )).
|
|
49
|
+
Digest::SHA256.digest(Digest::SHA256.digest( payload )).reverse_hth
|
|
48
50
|
end
|
|
49
51
|
alias generate_hash hash_from_payload
|
|
50
52
|
|
|
@@ -55,49 +57,49 @@ module Bitcoin
|
|
|
55
57
|
def add_out(output); (@out ||= []) << output; end
|
|
56
58
|
|
|
57
59
|
# parse raw binary data
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
in_size, tmp = Protocol.unpack_var_int(data[idx..-1])
|
|
62
|
-
idx += data[idx..-1].bytesize-tmp.bytesize
|
|
63
|
-
# raise "unkown transaction version: #{@ver}" unless @ver == 1
|
|
64
|
-
|
|
65
|
-
@in = (0...in_size).map{
|
|
66
|
-
txin = TxIn.new
|
|
67
|
-
idx += txin.parse_data(data[idx..-1])
|
|
68
|
-
txin
|
|
69
|
-
}
|
|
60
|
+
def parse_data_from_io(data)
|
|
61
|
+
buf = data.is_a?(String) ? StringIO.new(data) : data
|
|
62
|
+
payload_start = buf.pos
|
|
70
63
|
|
|
71
|
-
|
|
72
|
-
idx += data[idx..-1].bytesize-tmp.bytesize
|
|
64
|
+
@ver = buf.read(4).unpack("V")[0]
|
|
73
65
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
66
|
+
in_size = Protocol.unpack_var_int_from_io(buf)
|
|
67
|
+
@in = []
|
|
68
|
+
in_size.times{ @in << TxIn.from_io(buf) }
|
|
69
|
+
|
|
70
|
+
out_size = Protocol.unpack_var_int_from_io(buf)
|
|
71
|
+
@out = []
|
|
72
|
+
out_size.times{ @out << TxOut.from_io(buf) }
|
|
79
73
|
|
|
80
|
-
@lock_time =
|
|
74
|
+
@lock_time = buf.read(4).unpack("V")[0]
|
|
81
75
|
|
|
82
|
-
|
|
76
|
+
payload_end = buf.pos;
|
|
77
|
+
buf.seek(payload_start)
|
|
78
|
+
@payload = buf.read( payload_end-payload_start )
|
|
83
79
|
@hash = hash_from_payload(@payload)
|
|
84
80
|
|
|
85
|
-
if
|
|
86
|
-
true
|
|
81
|
+
if buf.eof?
|
|
82
|
+
true
|
|
87
83
|
else
|
|
88
|
-
data
|
|
84
|
+
data.is_a?(StringIO) ? buf : buf.read
|
|
89
85
|
end
|
|
90
86
|
end
|
|
91
87
|
|
|
88
|
+
alias :parse_data :parse_data_from_io
|
|
89
|
+
|
|
92
90
|
# output transaction in raw binary format
|
|
93
91
|
def to_payload
|
|
94
|
-
pin
|
|
95
|
-
|
|
92
|
+
pin = ""
|
|
93
|
+
@in.each{|input| pin << input.to_payload }
|
|
94
|
+
pout = ""
|
|
95
|
+
@out.each{|output| pout << output.to_payload }
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
[[@ver].pack("I"), in_size, pin, out_size, pout, [@lock_time].pack("I")].join
|
|
97
|
+
[@ver].pack("V") << Protocol.pack_var_int(@in.size) << pin << Protocol.pack_var_int(@out.size) << pout << [@lock_time].pack("V")
|
|
99
98
|
end
|
|
100
99
|
|
|
100
|
+
|
|
101
|
+
SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
|
|
102
|
+
|
|
101
103
|
# generate a signature hash for input +input_idx+.
|
|
102
104
|
# either pass the +outpoint_tx+ or the +script_pubkey+ directly.
|
|
103
105
|
def signature_hash_for_input(input_idx, outpoint_tx, script_pubkey=nil, hash_type=nil, drop_sigs=nil, script=nil)
|
|
@@ -105,46 +107,58 @@ module Bitcoin
|
|
|
105
107
|
# http://code.google.com/p/bitcoinj/source/browse/trunk/src/com/google/bitcoin/core/Script.java#318
|
|
106
108
|
# https://en.bitcoin.it/wiki/OP_CHECKSIG#How_it_works
|
|
107
109
|
# https://github.com/bitcoin/bitcoin/blob/c2e8c8acd8ae0c94c70b59f55169841ad195bb99/src/script.cpp#L1058
|
|
110
|
+
# https://en.bitcoin.it/wiki/OP_CHECKSIG
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
return "\x01".ljust(32, "\x00") if input_idx >= @in.size # ERROR: SignatureHash() : input_idx=%d out of range
|
|
113
|
+
|
|
114
|
+
hash_type ||= SIGHASH_TYPE[:all]
|
|
110
115
|
|
|
111
116
|
pin = @in.map.with_index{|input,idx|
|
|
112
117
|
if idx == input_idx
|
|
113
118
|
script_pubkey ||= outpoint_tx.out[ input.prev_out_index ].pk_script
|
|
114
|
-
script_pubkey =
|
|
115
|
-
script_pubkey = Bitcoin::Script.drop_signatures(script_pubkey, drop_sigs) if drop_sigs # array of signature to drop
|
|
119
|
+
script_pubkey = script if script # force binary aa script
|
|
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
|
|
116
122
|
input.to_payload(script_pubkey)
|
|
117
123
|
else
|
|
118
|
-
case hash_type
|
|
119
|
-
when
|
|
120
|
-
|
|
124
|
+
case (hash_type & 0x1f)
|
|
125
|
+
when SIGHASH_TYPE[:none]; input.to_payload("", "\x00\x00\x00\x00")
|
|
126
|
+
when SIGHASH_TYPE[:single]; input.to_payload("", "\x00\x00\x00\x00")
|
|
127
|
+
else; input.to_payload("")
|
|
121
128
|
end
|
|
122
129
|
end
|
|
123
|
-
}
|
|
130
|
+
}
|
|
124
131
|
|
|
125
|
-
pout = @out.map(&:to_payload)
|
|
132
|
+
pout = @out.map(&:to_payload)
|
|
133
|
+
in_size, out_size = Protocol.pack_var_int(@in.size), Protocol.pack_var_int(@out.size)
|
|
126
134
|
|
|
127
|
-
case hash_type
|
|
128
|
-
when
|
|
135
|
+
case (hash_type & 0x1f)
|
|
136
|
+
when SIGHASH_TYPE[:none]
|
|
129
137
|
pout = ""
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
138
|
+
out_size = Protocol.pack_var_int(0)
|
|
139
|
+
when SIGHASH_TYPE[:single]
|
|
140
|
+
return "\x01".ljust(32, "\x00") if input_idx >= @out.size # ERROR: SignatureHash() : input_idx=%d out of range (SIGHASH_SINGLE)
|
|
141
|
+
pout = @out[0...(input_idx+1)].map.with_index{|out,idx| (idx==input_idx) ? out.to_payload : out.to_null_payload }.join
|
|
142
|
+
out_size = Protocol.pack_var_int(input_idx+1)
|
|
133
143
|
end
|
|
134
144
|
|
|
135
|
-
|
|
145
|
+
if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
|
|
146
|
+
in_size, pin = Protocol.pack_var_int(1), [ pin[input_idx] ]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
buf = [ [@ver].pack("V"), in_size, pin, out_size, pout, [@lock_time, hash_type].pack("VV") ].join
|
|
136
150
|
Digest::SHA256.digest( Digest::SHA256.digest( buf ) )
|
|
137
151
|
end
|
|
138
152
|
|
|
139
153
|
# verify input signature +in_idx+ against the corresponding
|
|
140
154
|
# output in +outpoint_tx+
|
|
141
|
-
def verify_input_signature(in_idx, outpoint_tx)
|
|
155
|
+
def verify_input_signature(in_idx, outpoint_tx, block_timestamp=Time.now.to_i)
|
|
142
156
|
outpoint_idx = @in[in_idx].prev_out_index
|
|
143
157
|
script_sig = @in[in_idx].script_sig
|
|
144
158
|
script_pubkey = outpoint_tx.out[outpoint_idx].pk_script
|
|
145
159
|
script = script_sig + script_pubkey
|
|
146
160
|
|
|
147
|
-
Bitcoin::Script.new(script).run do |pubkey,sig,hash_type,drop_sigs,script|
|
|
161
|
+
Bitcoin::Script.new(script).run(block_timestamp) do |pubkey,sig,hash_type,drop_sigs,script|
|
|
148
162
|
# this IS the checksig callback, must return true/false
|
|
149
163
|
hash = signature_hash_for_input(in_idx, outpoint_tx, nil, hash_type, drop_sigs, script)
|
|
150
164
|
#hash = signature_hash_for_input(in_idx, nil, script_pubkey, hash_type, drop_sigs, script)
|
|
@@ -153,20 +167,21 @@ module Bitcoin
|
|
|
153
167
|
end
|
|
154
168
|
|
|
155
169
|
# convert to ruby hash (see also #from_hash)
|
|
156
|
-
def to_hash
|
|
170
|
+
def to_hash(options = {})
|
|
157
171
|
@hash ||= hash_from_payload(to_payload)
|
|
158
|
-
{
|
|
172
|
+
h = {
|
|
159
173
|
'hash' => @hash, 'ver' => @ver,
|
|
160
174
|
'vin_sz' => @in.size, 'vout_sz' => @out.size,
|
|
161
175
|
'lock_time' => @lock_time, 'size' => (@payload ||= to_payload).bytesize,
|
|
162
|
-
'in' => @in.map(
|
|
163
|
-
'out' => @out.map(
|
|
176
|
+
'in' => @in.map{|i| i.to_hash(options) },
|
|
177
|
+
'out' => @out.map{|o| o.to_hash(options) }
|
|
164
178
|
}
|
|
179
|
+
h
|
|
165
180
|
end
|
|
166
181
|
|
|
167
182
|
# generates rawblock json as seen in the block explorer.
|
|
168
183
|
def to_json(options = {:space => ''}, *a)
|
|
169
|
-
JSON.pretty_generate( to_hash, options )
|
|
184
|
+
JSON.pretty_generate( to_hash(options), options )
|
|
170
185
|
end
|
|
171
186
|
|
|
172
187
|
# write json representation to a file
|
|
@@ -199,7 +214,45 @@ module Bitcoin
|
|
|
199
214
|
|
|
200
215
|
# read json block from a file
|
|
201
216
|
def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
|
|
202
|
-
end
|
|
203
217
|
|
|
218
|
+
def validator(store, block = nil)
|
|
219
|
+
@validator ||= Bitcoin::Validation::Tx.new(self, store, block)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def minimum_relay_fee; calculate_minimum_fee(1_000, true, :relay); end
|
|
223
|
+
def minimum_block_fee; calculate_minimum_fee(1_000, true, :block); end
|
|
224
|
+
|
|
225
|
+
def calculate_minimum_fee(block_size=1, allow_free=true, mode=:block)
|
|
226
|
+
base_fee = (mode == :relay) ? Bitcoin.network[:min_relay_tx_fee] : Bitcoin.network[:min_tx_fee]
|
|
227
|
+
tx_size = to_payload.bytesize
|
|
228
|
+
new_block_size = block_size + tx_size
|
|
229
|
+
min_fee = (1 + tx_size / 1_000) * base_fee
|
|
230
|
+
|
|
231
|
+
if allow_free
|
|
232
|
+
if block_size == 1
|
|
233
|
+
min_fee = 0 if tx_size < 10_000
|
|
234
|
+
else
|
|
235
|
+
min_fee = 0 if new_block_size < 27_000
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
if min_fee < base_fee
|
|
240
|
+
outputs.each{|output| (min_fee = base_fee; break) if output.value < Bitcoin::CENT }
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
if block_size != 1 && new_block_size >= (Bitcoin::MAX_BLOCK_SIZE_GEN/2)
|
|
244
|
+
#return Bitcoin::network[:max_money] if new_block_size >= Bitcoin::MAX_BLOCK_SIZE_GEN
|
|
245
|
+
min_fee *= Bitcoin::MAX_BLOCK_SIZE_GEN / (Bitcoin::MAX_BLOCK_SIZE_GEN - new_block_size)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
min_fee = Bitcoin::network[:max_money] unless min_fee.between?(0, Bitcoin::network[:max_money])
|
|
249
|
+
min_fee
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def is_coinbase?
|
|
253
|
+
inputs.size == 1 and inputs.first.coinbase?
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
end
|
|
204
257
|
end
|
|
205
258
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
1
3
|
module Bitcoin
|
|
2
4
|
module Protocol
|
|
3
5
|
|
|
@@ -12,10 +14,19 @@ module Bitcoin
|
|
|
12
14
|
# script_sig input Script (signature)
|
|
13
15
|
attr_accessor :script_sig, :script_sig_length
|
|
14
16
|
|
|
17
|
+
# signature hash and the address of the key that needs to sign it
|
|
18
|
+
# (used when dealing with unsigned or partly signed tx)
|
|
19
|
+
attr_accessor :sig_hash, :sig_address
|
|
20
|
+
|
|
21
|
+
alias :script :script_sig
|
|
22
|
+
alias :script_length :script_sig_length
|
|
23
|
+
|
|
15
24
|
# sequence
|
|
16
25
|
attr_accessor :sequence
|
|
17
26
|
|
|
18
27
|
DEFAULT_SEQUENCE = "\xff\xff\xff\xff"
|
|
28
|
+
NULL_HASH = "\x00"*32
|
|
29
|
+
COINBASE_INDEX = 0xffffffff
|
|
19
30
|
|
|
20
31
|
def initialize *args
|
|
21
32
|
@prev_out, @prev_out_index, @script_sig_length,
|
|
@@ -33,32 +44,34 @@ module Bitcoin
|
|
|
33
44
|
|
|
34
45
|
# parse raw binary data for transaction input
|
|
35
46
|
def parse_data(data)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
buf = data.is_a?(String) ? StringIO.new(data) : data
|
|
48
|
+
parse_data_from_io(buf)
|
|
49
|
+
buf.pos
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.from_io(buf)
|
|
53
|
+
txin = new; txin.parse_data_from_io(buf); txin
|
|
43
54
|
end
|
|
44
55
|
|
|
45
|
-
|
|
56
|
+
def parse_data_from_io(buf)
|
|
57
|
+
@prev_out, @prev_out_index = buf.read(36).unpack("a32V")
|
|
58
|
+
@script_sig_length = Protocol.unpack_var_int_from_io(buf)
|
|
59
|
+
@script_sig = buf.read(@script_sig_length)
|
|
60
|
+
@sequence = buf.read(4)
|
|
61
|
+
end
|
|
46
62
|
|
|
47
63
|
def to_payload(script=@script_sig, sequence=@sequence)
|
|
48
|
-
|
|
49
|
-
buf << Protocol.pack_var_int(script.bytesize)
|
|
50
|
-
buf << script if script.bytesize > 0
|
|
51
|
-
buf << (sequence || DEFAULT_SEQUENCE)
|
|
64
|
+
[@prev_out, @prev_out_index].pack("a32V") << Protocol.pack_var_int(script.bytesize) << script << (sequence || DEFAULT_SEQUENCE)
|
|
52
65
|
end
|
|
53
66
|
|
|
54
|
-
def to_hash
|
|
55
|
-
t = { 'prev_out' => { 'hash' => @prev_out.
|
|
67
|
+
def to_hash(options = {})
|
|
68
|
+
t = { 'prev_out' => { 'hash' => @prev_out.reverse_hth, 'n' => @prev_out_index } }
|
|
56
69
|
if coinbase?
|
|
57
70
|
t['coinbase'] = @script_sig.unpack("H*")[0]
|
|
58
71
|
else # coinbase tx
|
|
59
72
|
t['scriptSig'] = Bitcoin::Script.new(@script_sig).to_string
|
|
60
|
-
t['sequence'] = @sequence.unpack("I")[0] unless @sequence == "\xff\xff\xff\xff"
|
|
61
73
|
end
|
|
74
|
+
t['sequence'] = @sequence.unpack("V")[0] unless @sequence == "\xff\xff\xff\xff"
|
|
62
75
|
t
|
|
63
76
|
end
|
|
64
77
|
|
|
@@ -66,22 +79,25 @@ module Bitcoin
|
|
|
66
79
|
txin = TxIn.new([ input['prev_out']['hash'] ].pack('H*').reverse, input['prev_out']['n'])
|
|
67
80
|
if input['coinbase']
|
|
68
81
|
txin.script_sig = [ input['coinbase'] ].pack("H*")
|
|
69
|
-
txin.sequence = "\xff\xff\xff\xff"
|
|
70
82
|
else
|
|
71
83
|
txin.script_sig = Script.binary_from_string(input['scriptSig'])
|
|
72
|
-
txin.sequence = [ input['sequence'] || 0xffffffff ].pack("I")
|
|
73
84
|
end
|
|
85
|
+
txin.sequence = [ input['sequence'] || 0xffffffff ].pack("V")
|
|
74
86
|
txin
|
|
75
87
|
end
|
|
76
88
|
|
|
89
|
+
def self.from_hex_hash(hash, index)
|
|
90
|
+
TxIn.new([hash].pack("H*").reverse, index, 0)
|
|
91
|
+
end
|
|
92
|
+
|
|
77
93
|
# previous output in hex
|
|
78
94
|
def previous_output
|
|
79
|
-
@prev_out.
|
|
95
|
+
@prev_out.reverse_hth
|
|
80
96
|
end
|
|
81
97
|
|
|
82
98
|
# check if input is coinbase
|
|
83
99
|
def coinbase?
|
|
84
|
-
(@prev_out_index ==
|
|
100
|
+
(@prev_out_index == COINBASE_INDEX) && (@prev_out == NULL_HASH)
|
|
85
101
|
end
|
|
86
102
|
|
|
87
103
|
# set script_sig and script_sig_length
|
|
@@ -91,6 +107,10 @@ module Bitcoin
|
|
|
91
107
|
end
|
|
92
108
|
alias :script= :script_sig=
|
|
93
109
|
|
|
110
|
+
def add_signature_pubkey_script(sig, pubkey_hex)
|
|
111
|
+
self.script = Bitcoin::Script.to_signature_pubkey_script(sig, [pubkey_hex].pack("H*"))
|
|
112
|
+
end
|
|
113
|
+
|
|
94
114
|
end
|
|
95
115
|
|
|
96
116
|
end
|