bitcoin-ruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +12 -0
- data/COPYING +18 -0
- data/Gemfile +4 -0
- data/README.rdoc +189 -0
- data/Rakefile +104 -0
- data/bin/bitcoin_dns_seed +130 -0
- data/bin/bitcoin_gui +80 -0
- data/bin/bitcoin_node +174 -0
- data/bin/bitcoin_shell +12 -0
- data/bin/bitcoin_wallet +323 -0
- data/bitcoin-ruby.gemspec +27 -0
- data/concept-examples/blockchain-pow.rb +151 -0
- data/doc/CONFIG.rdoc +66 -0
- data/doc/EXAMPLES.rdoc +9 -0
- data/doc/NODE.rdoc +35 -0
- data/doc/STORAGE.rdoc +21 -0
- data/doc/WALLET.rdoc +102 -0
- data/examples/balance.rb +60 -0
- data/examples/bbe_verify_tx.rb +55 -0
- data/examples/connect.rb +36 -0
- data/examples/relay_tx.rb +22 -0
- data/examples/verify_tx.rb +57 -0
- data/lib/bitcoin.rb +370 -0
- data/lib/bitcoin/builder.rb +266 -0
- data/lib/bitcoin/config.rb +56 -0
- data/lib/bitcoin/connection.rb +126 -0
- data/lib/bitcoin/ffi/openssl.rb +121 -0
- data/lib/bitcoin/gui/addr_view.rb +42 -0
- data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
- data/lib/bitcoin/gui/bitcoin-ruby.svg +80 -0
- data/lib/bitcoin/gui/conn_view.rb +36 -0
- data/lib/bitcoin/gui/connection.rb +68 -0
- data/lib/bitcoin/gui/em_gtk.rb +28 -0
- data/lib/bitcoin/gui/gui.builder +1643 -0
- data/lib/bitcoin/gui/gui.rb +290 -0
- data/lib/bitcoin/gui/helpers.rb +113 -0
- data/lib/bitcoin/gui/tree_view.rb +82 -0
- data/lib/bitcoin/gui/tx_view.rb +67 -0
- data/lib/bitcoin/key.rb +125 -0
- data/lib/bitcoin/logger.rb +65 -0
- data/lib/bitcoin/network/command_client.rb +93 -0
- data/lib/bitcoin/network/command_handler.rb +179 -0
- data/lib/bitcoin/network/connection_handler.rb +274 -0
- data/lib/bitcoin/network/node.rb +399 -0
- data/lib/bitcoin/protocol.rb +140 -0
- data/lib/bitcoin/protocol/address.rb +48 -0
- data/lib/bitcoin/protocol/alert.rb +47 -0
- data/lib/bitcoin/protocol/block.rb +154 -0
- data/lib/bitcoin/protocol/handler.rb +38 -0
- data/lib/bitcoin/protocol/parser.rb +148 -0
- data/lib/bitcoin/protocol/tx.rb +205 -0
- data/lib/bitcoin/protocol/txin.rb +97 -0
- data/lib/bitcoin/protocol/txout.rb +73 -0
- data/lib/bitcoin/protocol/version.rb +70 -0
- data/lib/bitcoin/script.rb +634 -0
- data/lib/bitcoin/storage/dummy.rb +164 -0
- data/lib/bitcoin/storage/models.rb +133 -0
- data/lib/bitcoin/storage/sequel.rb +335 -0
- data/lib/bitcoin/storage/sequel_store/sequel_migrations.rb +84 -0
- data/lib/bitcoin/storage/storage.rb +243 -0
- data/lib/bitcoin/version.rb +3 -0
- data/lib/bitcoin/wallet/coinselector.rb +30 -0
- data/lib/bitcoin/wallet/keygenerator.rb +75 -0
- data/lib/bitcoin/wallet/keystore.rb +203 -0
- data/lib/bitcoin/wallet/txdp.rb +116 -0
- data/lib/bitcoin/wallet/wallet.rb +243 -0
- data/spec/bitcoin/bitcoin_spec.rb +472 -0
- data/spec/bitcoin/builder_spec.rb +90 -0
- data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +27 -0
- data/spec/bitcoin/fixtures/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +23 -0
- data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +27 -0
- data/spec/bitcoin/fixtures/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +45 -0
- data/spec/bitcoin/fixtures/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +34 -0
- data/spec/bitcoin/fixtures/rawblock-0.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-0.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-1.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-1.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-131025.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-131025.json +5063 -0
- data/spec/bitcoin/fixtures/rawblock-170.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-170.json +68 -0
- data/spec/bitcoin/fixtures/rawblock-9.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-9.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-26478.bin +0 -0
- data/spec/bitcoin/fixtures/rawblock-testnet-26478.json +64 -0
- data/spec/bitcoin/fixtures/rawtx-01.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-01.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-02.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-02.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-03.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-03.json +48 -0
- data/spec/bitcoin/fixtures/rawtx-04.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-05.json +23 -0
- data/spec/bitcoin/fixtures/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +23 -0
- data/spec/bitcoin/fixtures/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +37 -0
- data/spec/bitcoin/fixtures/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +24 -0
- data/spec/bitcoin/fixtures/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +23 -0
- data/spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +27 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +41 -0
- data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
- data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
- data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
- data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
- data/spec/bitcoin/fixtures/txdp-1.txt +32 -0
- data/spec/bitcoin/fixtures/txdp-2-signed.txt +19 -0
- data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +14 -0
- data/spec/bitcoin/key_spec.rb +123 -0
- data/spec/bitcoin/network_spec.rb +48 -0
- data/spec/bitcoin/protocol/addr_spec.rb +68 -0
- data/spec/bitcoin/protocol/alert_spec.rb +20 -0
- data/spec/bitcoin/protocol/block_spec.rb +101 -0
- data/spec/bitcoin/protocol/inv_spec.rb +124 -0
- data/spec/bitcoin/protocol/ping_spec.rb +49 -0
- data/spec/bitcoin/protocol/tx_spec.rb +226 -0
- data/spec/bitcoin/protocol/version_spec.rb +77 -0
- data/spec/bitcoin/reorg_spec.rb +129 -0
- data/spec/bitcoin/script/opcodes_spec.rb +417 -0
- data/spec/bitcoin/script/script_spec.rb +246 -0
- data/spec/bitcoin/spec_helper.rb +36 -0
- data/spec/bitcoin/storage_spec.rb +229 -0
- data/spec/bitcoin/wallet/coinselector_spec.rb +35 -0
- data/spec/bitcoin/wallet/keygenerator_spec.rb +64 -0
- data/spec/bitcoin/wallet/keystore_spec.rb +188 -0
- data/spec/bitcoin/wallet/txdp_spec.rb +74 -0
- data/spec/bitcoin/wallet/wallet_spec.rb +207 -0
- metadata +295 -0
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'bitcoin/script'
|
2
|
+
|
3
|
+
module Bitcoin
|
4
|
+
module Protocol
|
5
|
+
|
6
|
+
class Tx
|
7
|
+
|
8
|
+
# transaction hash
|
9
|
+
attr_reader :hash
|
10
|
+
|
11
|
+
# inputs (Array of TxIn)
|
12
|
+
attr_reader :in
|
13
|
+
|
14
|
+
# outputs (Array of TxOut)
|
15
|
+
attr_reader :out
|
16
|
+
|
17
|
+
# raw protocol payload
|
18
|
+
attr_reader :payload
|
19
|
+
|
20
|
+
# version (usually 1)
|
21
|
+
attr_accessor :ver
|
22
|
+
|
23
|
+
# lock time
|
24
|
+
attr_accessor :lock_time
|
25
|
+
|
26
|
+
alias :inputs :in
|
27
|
+
alias :outputs :out
|
28
|
+
|
29
|
+
# compare to another tx
|
30
|
+
def ==(other)
|
31
|
+
@hash == other.hash
|
32
|
+
end
|
33
|
+
|
34
|
+
# return the tx hash in binary format
|
35
|
+
def binary_hash
|
36
|
+
[@hash].pack("H*").reverse
|
37
|
+
end
|
38
|
+
|
39
|
+
# create tx from raw binary +data+
|
40
|
+
def initialize(data=nil)
|
41
|
+
@ver, @lock_time, @in, @out = 1, 0, [], []
|
42
|
+
parse_data(data) if data
|
43
|
+
end
|
44
|
+
|
45
|
+
# generate the tx hash for given +payload+ in hex format
|
46
|
+
def hash_from_payload(payload)
|
47
|
+
Digest::SHA256.digest(Digest::SHA256.digest( payload )).reverse.unpack("H*")[0]
|
48
|
+
end
|
49
|
+
alias generate_hash hash_from_payload
|
50
|
+
|
51
|
+
# add an input
|
52
|
+
def add_in(input); (@in ||= []) << input; end
|
53
|
+
|
54
|
+
# add an output
|
55
|
+
def add_out(output); (@out ||= []) << output; end
|
56
|
+
|
57
|
+
# parse raw binary data
|
58
|
+
def parse_data(data)
|
59
|
+
@ver = data.unpack("I")[0]
|
60
|
+
idx = 4
|
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
|
+
}
|
70
|
+
|
71
|
+
out_size, tmp = Protocol.unpack_var_int(data[idx..-1])
|
72
|
+
idx += data[idx..-1].bytesize-tmp.bytesize
|
73
|
+
|
74
|
+
@out = (0...out_size).map{
|
75
|
+
txout = TxOut.new
|
76
|
+
idx += txout.parse_data(data[idx..-1])
|
77
|
+
txout
|
78
|
+
}
|
79
|
+
|
80
|
+
@lock_time = data[idx...idx+=4].unpack("I")[0]
|
81
|
+
|
82
|
+
@payload = data[0...idx]
|
83
|
+
@hash = hash_from_payload(@payload)
|
84
|
+
|
85
|
+
if data[idx] == nil
|
86
|
+
true # reached the end.
|
87
|
+
else
|
88
|
+
data[idx..-1] # rest of buffer.
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# output transaction in raw binary format
|
93
|
+
def to_payload
|
94
|
+
pin = @in.map(&:to_payload).join
|
95
|
+
pout = @out.map(&:to_payload).join
|
96
|
+
|
97
|
+
in_size, out_size = Protocol.pack_var_int(@in.size), Protocol.pack_var_int(@out.size)
|
98
|
+
[[@ver].pack("I"), in_size, pin, out_size, pout, [@lock_time].pack("I")].join
|
99
|
+
end
|
100
|
+
|
101
|
+
# generate a signature hash for input +input_idx+.
|
102
|
+
# either pass the +outpoint_tx+ or the +script_pubkey+ directly.
|
103
|
+
def signature_hash_for_input(input_idx, outpoint_tx, script_pubkey=nil, hash_type=nil, drop_sigs=nil, script=nil)
|
104
|
+
# https://github.com/bitcoin/bitcoin/blob/e071a3f6c06f41068ad17134189a4ac3073ef76b/script.cpp#L834
|
105
|
+
# http://code.google.com/p/bitcoinj/source/browse/trunk/src/com/google/bitcoin/core/Script.java#318
|
106
|
+
# https://en.bitcoin.it/wiki/OP_CHECKSIG#How_it_works
|
107
|
+
# https://github.com/bitcoin/bitcoin/blob/c2e8c8acd8ae0c94c70b59f55169841ad195bb99/src/script.cpp#L1058
|
108
|
+
|
109
|
+
hash_type ||= 1 # 1: ALL, 2: NONE, 3: SINGLE
|
110
|
+
|
111
|
+
pin = @in.map.with_index{|input,idx|
|
112
|
+
if idx == input_idx
|
113
|
+
script_pubkey ||= outpoint_tx.out[ input.prev_out_index ].pk_script
|
114
|
+
script_pubkey = Bitcoin::Script.binary_from_string(script) if script # force this string a script
|
115
|
+
script_pubkey = Bitcoin::Script.drop_signatures(script_pubkey, drop_sigs) if drop_sigs # array of signature to drop
|
116
|
+
input.to_payload(script_pubkey)
|
117
|
+
else
|
118
|
+
case hash_type
|
119
|
+
when 2; input.to_payload("", "\x00\x00\x00\x00")
|
120
|
+
else; input.to_payload("")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
}.join
|
124
|
+
|
125
|
+
pout = @out.map(&:to_payload).join
|
126
|
+
|
127
|
+
case hash_type
|
128
|
+
when 2
|
129
|
+
pout = ""
|
130
|
+
in_size, out_size = Protocol.pack_var_int(@in.size), Protocol.pack_var_int(0)
|
131
|
+
else
|
132
|
+
in_size, out_size = Protocol.pack_var_int(@in.size), Protocol.pack_var_int(@out.size)
|
133
|
+
end
|
134
|
+
|
135
|
+
buf = [ [@ver].pack("I"), in_size, pin, out_size, pout, [@lock_time, hash_type].pack("II") ].join
|
136
|
+
Digest::SHA256.digest( Digest::SHA256.digest( buf ) )
|
137
|
+
end
|
138
|
+
|
139
|
+
# verify input signature +in_idx+ against the corresponding
|
140
|
+
# output in +outpoint_tx+
|
141
|
+
def verify_input_signature(in_idx, outpoint_tx)
|
142
|
+
outpoint_idx = @in[in_idx].prev_out_index
|
143
|
+
script_sig = @in[in_idx].script_sig
|
144
|
+
script_pubkey = outpoint_tx.out[outpoint_idx].pk_script
|
145
|
+
script = script_sig + script_pubkey
|
146
|
+
|
147
|
+
Bitcoin::Script.new(script).run do |pubkey,sig,hash_type,drop_sigs,script|
|
148
|
+
# this IS the checksig callback, must return true/false
|
149
|
+
hash = signature_hash_for_input(in_idx, outpoint_tx, nil, hash_type, drop_sigs, script)
|
150
|
+
#hash = signature_hash_for_input(in_idx, nil, script_pubkey, hash_type, drop_sigs, script)
|
151
|
+
Bitcoin.verify_signature( hash, sig, pubkey.unpack("H*")[0] )
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# convert to ruby hash (see also #from_hash)
|
156
|
+
def to_hash
|
157
|
+
@hash ||= hash_from_payload(to_payload)
|
158
|
+
{
|
159
|
+
'hash' => @hash, 'ver' => @ver,
|
160
|
+
'vin_sz' => @in.size, 'vout_sz' => @out.size,
|
161
|
+
'lock_time' => @lock_time, 'size' => (@payload ||= to_payload).bytesize,
|
162
|
+
'in' => @in.map(&:to_hash),
|
163
|
+
'out' => @out.map(&:to_hash)
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
# generates rawblock json as seen in the block explorer.
|
168
|
+
def to_json(options = {:space => ''}, *a)
|
169
|
+
JSON.pretty_generate( to_hash, options )
|
170
|
+
end
|
171
|
+
|
172
|
+
# write json representation to a file
|
173
|
+
# (see also #to_json)
|
174
|
+
def to_json_file(path)
|
175
|
+
File.open(path, 'wb'){|f| f.print to_json; }
|
176
|
+
end
|
177
|
+
|
178
|
+
# parse ruby hash (see also #to_hash)
|
179
|
+
def self.from_hash(h)
|
180
|
+
tx = new(nil)
|
181
|
+
tx.ver, tx.lock_time = *h.values_at('ver', 'lock_time')
|
182
|
+
h['in'] .each{|input| tx.add_in TxIn.from_hash(input) }
|
183
|
+
h['out'].each{|output| tx.add_out TxOut.from_hash(output) }
|
184
|
+
tx.instance_eval{ @hash = hash_from_payload(@payload = to_payload) }
|
185
|
+
tx
|
186
|
+
end
|
187
|
+
|
188
|
+
# convert ruby hash to raw binary
|
189
|
+
def self.binary_from_hash(h); from_hash(h).to_payload; end
|
190
|
+
|
191
|
+
# parse json representation
|
192
|
+
def self.from_json(json_string); from_hash( JSON.load(json_string) ); end
|
193
|
+
|
194
|
+
# convert json representation to raw binary
|
195
|
+
def self.binary_from_json(json_string); from_json(json_string).to_payload; end
|
196
|
+
|
197
|
+
# read binary block from a file
|
198
|
+
def self.from_file(path); new( Bitcoin::Protocol.read_binary_file(path) ); end
|
199
|
+
|
200
|
+
# read json block from a file
|
201
|
+
def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Protocol
|
3
|
+
|
4
|
+
class TxIn
|
5
|
+
|
6
|
+
# previous output hash
|
7
|
+
attr_accessor :prev_out
|
8
|
+
|
9
|
+
# previous output index
|
10
|
+
attr_accessor :prev_out_index
|
11
|
+
|
12
|
+
# script_sig input Script (signature)
|
13
|
+
attr_accessor :script_sig, :script_sig_length
|
14
|
+
|
15
|
+
# sequence
|
16
|
+
attr_accessor :sequence
|
17
|
+
|
18
|
+
DEFAULT_SEQUENCE = "\xff\xff\xff\xff"
|
19
|
+
|
20
|
+
def initialize *args
|
21
|
+
@prev_out, @prev_out_index, @script_sig_length,
|
22
|
+
@script_sig, @sequence = *args
|
23
|
+
@sequence ||= DEFAULT_SEQUENCE
|
24
|
+
end
|
25
|
+
|
26
|
+
# compare to another txout
|
27
|
+
def ==(other)
|
28
|
+
@prev_out == other.prev_out &&
|
29
|
+
@prev_out_index == other.prev_out_index &&
|
30
|
+
@script_sig == other.script_sig &&
|
31
|
+
@sequence == other.sequence
|
32
|
+
end
|
33
|
+
|
34
|
+
# parse raw binary data for transaction input
|
35
|
+
def parse_data(data)
|
36
|
+
idx = 0
|
37
|
+
@prev_out, @prev_out_index = data[idx...idx+=36].unpack("a32I")
|
38
|
+
@script_sig_length, tmp = Protocol.unpack_var_int(data[idx..-1])
|
39
|
+
idx += data[idx..-1].bytesize - tmp.bytesize
|
40
|
+
@script_sig = data[idx...idx+=@script_sig_length]
|
41
|
+
@sequence = data[idx...idx+=4]
|
42
|
+
idx
|
43
|
+
end
|
44
|
+
|
45
|
+
alias :parse_payload :parse_data
|
46
|
+
|
47
|
+
def to_payload(script=@script_sig, sequence=@sequence)
|
48
|
+
buf = [ @prev_out, @prev_out_index ].pack("a32I")
|
49
|
+
buf << Protocol.pack_var_int(script.bytesize)
|
50
|
+
buf << script if script.bytesize > 0
|
51
|
+
buf << (sequence || DEFAULT_SEQUENCE)
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_hash
|
55
|
+
t = { 'prev_out' => { 'hash' => @prev_out.reverse.unpack("H*")[0], 'n' => @prev_out_index } }
|
56
|
+
if coinbase?
|
57
|
+
t['coinbase'] = @script_sig.unpack("H*")[0]
|
58
|
+
else # coinbase tx
|
59
|
+
t['scriptSig'] = Bitcoin::Script.new(@script_sig).to_string
|
60
|
+
t['sequence'] = @sequence.unpack("I")[0] unless @sequence == "\xff\xff\xff\xff"
|
61
|
+
end
|
62
|
+
t
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.from_hash(input)
|
66
|
+
txin = TxIn.new([ input['prev_out']['hash'] ].pack('H*').reverse, input['prev_out']['n'])
|
67
|
+
if input['coinbase']
|
68
|
+
txin.script_sig = [ input['coinbase'] ].pack("H*")
|
69
|
+
txin.sequence = "\xff\xff\xff\xff"
|
70
|
+
else
|
71
|
+
txin.script_sig = Script.binary_from_string(input['scriptSig'])
|
72
|
+
txin.sequence = [ input['sequence'] || 0xffffffff ].pack("I")
|
73
|
+
end
|
74
|
+
txin
|
75
|
+
end
|
76
|
+
|
77
|
+
# previous output in hex
|
78
|
+
def previous_output
|
79
|
+
@prev_out.reverse.unpack("H*")[0]
|
80
|
+
end
|
81
|
+
|
82
|
+
# check if input is coinbase
|
83
|
+
def coinbase?
|
84
|
+
(@prev_out_index == 4294967295) && (@prev_out == "\x00"*32)
|
85
|
+
end
|
86
|
+
|
87
|
+
# set script_sig and script_sig_length
|
88
|
+
def script_sig=(script_sig)
|
89
|
+
@script_sig_length = script_sig.bytesize
|
90
|
+
@script_sig = script_sig
|
91
|
+
end
|
92
|
+
alias :script= :script_sig=
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Protocol
|
3
|
+
|
4
|
+
class TxOut
|
5
|
+
|
6
|
+
# output value (in base units; "satoshi")
|
7
|
+
attr_accessor :value
|
8
|
+
|
9
|
+
# pk_script output Script
|
10
|
+
attr_accessor :pk_script, :pk_script_length
|
11
|
+
|
12
|
+
def initialize *args
|
13
|
+
if args.size == 2
|
14
|
+
@value, @pk_script_length, @pk_script = args[0], args[1].bytesize, args[1]
|
15
|
+
else
|
16
|
+
@value, @pk_script_length, @pk_script = *args
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(other)
|
21
|
+
@value == other.value && @pk_script == other.pk_script
|
22
|
+
end
|
23
|
+
|
24
|
+
# parse raw binary data for transaction output
|
25
|
+
def parse_data(data)
|
26
|
+
idx = 0
|
27
|
+
@value = data[idx...idx+=8].unpack("Q")[0]
|
28
|
+
@pk_script_length, tmp = Protocol.unpack_var_int(data[idx..-1])
|
29
|
+
idx += data[idx..-1].bytesize - tmp.bytesize
|
30
|
+
@pk_script = data[idx...idx+=@pk_script_length]
|
31
|
+
idx
|
32
|
+
end
|
33
|
+
|
34
|
+
alias :parse_payload :parse_data
|
35
|
+
|
36
|
+
def to_payload
|
37
|
+
buf = [ @value ].pack("Q")
|
38
|
+
buf << Protocol.pack_var_int(@pk_script_length)
|
39
|
+
buf << @pk_script if @pk_script_length > 0
|
40
|
+
buf
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_hash
|
44
|
+
{ 'value' => "%.8f" % (@value / 100000000.0),
|
45
|
+
'scriptPubKey' => Bitcoin::Script.new(@pk_script).to_string }
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.from_hash(output)
|
49
|
+
amount = output['value'].gsub('.','').to_i
|
50
|
+
script = Script.binary_from_string(output['scriptPubKey'])
|
51
|
+
new(amount, script)
|
52
|
+
end
|
53
|
+
# set pk_script and pk_script_length
|
54
|
+
def pk_script=(script)
|
55
|
+
@pk_script_length, @pk_script = script.bytesize, script
|
56
|
+
end
|
57
|
+
|
58
|
+
alias :amount :value
|
59
|
+
alias :amount= :value=
|
60
|
+
alias :script :pk_script
|
61
|
+
alias :script= :pk_script=
|
62
|
+
|
63
|
+
|
64
|
+
# create output spending +value+ btc (base units) to +address+
|
65
|
+
def self.value_to_address(value, address)
|
66
|
+
pk_script = Bitcoin::Script.to_address_script(address)
|
67
|
+
new(value, pk_script)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Protocol
|
3
|
+
|
4
|
+
class Version
|
5
|
+
attr_reader :fields
|
6
|
+
def initialize(opts={})
|
7
|
+
@fields = {
|
8
|
+
:version => Bitcoin::Protocol::VERSION,
|
9
|
+
:services => 1,
|
10
|
+
:time => Time.now.tv_sec,
|
11
|
+
:from => "127.0.0.1:8333",
|
12
|
+
:to => "127.0.0.1:8333",
|
13
|
+
:nonce => Bitcoin::Protocol::Uniq,
|
14
|
+
:user_agent => "/bitcoin-ruby:#{Bitcoin::VERSION}/",
|
15
|
+
:last_block => 0 # 188617
|
16
|
+
}.merge( opts.reject{|k,v| v == nil } )
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_payload
|
20
|
+
payload = [
|
21
|
+
@fields.values_at(:version, :services, :time).pack("IQQ"),
|
22
|
+
pack_address_field(@fields[:from]),
|
23
|
+
pack_address_field(@fields[:to]),
|
24
|
+
@fields.values_at(:nonce).pack("Q"),
|
25
|
+
Protocol.pack_var_string(@fields[:user_agent]),
|
26
|
+
@fields.values_at(:last_block).pack("I")
|
27
|
+
].join
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_pkt
|
31
|
+
Bitcoin::Protocol.pkt("version", to_payload)
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse(payload)
|
35
|
+
version, services, timestamp, to, from, nonce, payload = payload.unpack("Ia8Qa26a26Qa*")
|
36
|
+
to, from = unpack_address_field(to), unpack_address_field(from)
|
37
|
+
user_agent, payload = Protocol.unpack_var_string(payload)
|
38
|
+
last_block = payload.unpack("I")[0]
|
39
|
+
|
40
|
+
@fields = {
|
41
|
+
:version => version, :services => services, :time => timestamp,
|
42
|
+
:from => from, :to => to, :nonce => nonce,
|
43
|
+
:user_agent => user_agent, :last_block => last_block
|
44
|
+
}
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def unpack_address_field(payload)
|
49
|
+
ip, port = payload.unpack("x8x12a4n")
|
50
|
+
"#{ip.unpack("C*").join(".")}:#{port}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def pack_address_field(addr_str)
|
54
|
+
host, port = addr_str.split(":")
|
55
|
+
port = port ? port.to_i : 8333
|
56
|
+
sockaddr = Socket.pack_sockaddr_in(port, host)
|
57
|
+
#raise "invalid IPv4 Address: #{addr}" unless sockaddr[0...2] == "\x02\x00"
|
58
|
+
port, host = sockaddr[2...4], sockaddr[4...8]
|
59
|
+
[[1].pack("Q"), "\x00"*10, "\xFF\xFF", host, port].join
|
60
|
+
end
|
61
|
+
|
62
|
+
def uptime
|
63
|
+
@fields[:time] - Time.now.tv_sec
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.parse(payload); new.parse(payload); end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|