bitcoin-ruby 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. data/.gitignore +12 -0
  2. data/COPYING +18 -0
  3. data/Gemfile +4 -0
  4. data/README.rdoc +189 -0
  5. data/Rakefile +104 -0
  6. data/bin/bitcoin_dns_seed +130 -0
  7. data/bin/bitcoin_gui +80 -0
  8. data/bin/bitcoin_node +174 -0
  9. data/bin/bitcoin_shell +12 -0
  10. data/bin/bitcoin_wallet +323 -0
  11. data/bitcoin-ruby.gemspec +27 -0
  12. data/concept-examples/blockchain-pow.rb +151 -0
  13. data/doc/CONFIG.rdoc +66 -0
  14. data/doc/EXAMPLES.rdoc +9 -0
  15. data/doc/NODE.rdoc +35 -0
  16. data/doc/STORAGE.rdoc +21 -0
  17. data/doc/WALLET.rdoc +102 -0
  18. data/examples/balance.rb +60 -0
  19. data/examples/bbe_verify_tx.rb +55 -0
  20. data/examples/connect.rb +36 -0
  21. data/examples/relay_tx.rb +22 -0
  22. data/examples/verify_tx.rb +57 -0
  23. data/lib/bitcoin.rb +370 -0
  24. data/lib/bitcoin/builder.rb +266 -0
  25. data/lib/bitcoin/config.rb +56 -0
  26. data/lib/bitcoin/connection.rb +126 -0
  27. data/lib/bitcoin/ffi/openssl.rb +121 -0
  28. data/lib/bitcoin/gui/addr_view.rb +42 -0
  29. data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
  30. data/lib/bitcoin/gui/bitcoin-ruby.svg +80 -0
  31. data/lib/bitcoin/gui/conn_view.rb +36 -0
  32. data/lib/bitcoin/gui/connection.rb +68 -0
  33. data/lib/bitcoin/gui/em_gtk.rb +28 -0
  34. data/lib/bitcoin/gui/gui.builder +1643 -0
  35. data/lib/bitcoin/gui/gui.rb +290 -0
  36. data/lib/bitcoin/gui/helpers.rb +113 -0
  37. data/lib/bitcoin/gui/tree_view.rb +82 -0
  38. data/lib/bitcoin/gui/tx_view.rb +67 -0
  39. data/lib/bitcoin/key.rb +125 -0
  40. data/lib/bitcoin/logger.rb +65 -0
  41. data/lib/bitcoin/network/command_client.rb +93 -0
  42. data/lib/bitcoin/network/command_handler.rb +179 -0
  43. data/lib/bitcoin/network/connection_handler.rb +274 -0
  44. data/lib/bitcoin/network/node.rb +399 -0
  45. data/lib/bitcoin/protocol.rb +140 -0
  46. data/lib/bitcoin/protocol/address.rb +48 -0
  47. data/lib/bitcoin/protocol/alert.rb +47 -0
  48. data/lib/bitcoin/protocol/block.rb +154 -0
  49. data/lib/bitcoin/protocol/handler.rb +38 -0
  50. data/lib/bitcoin/protocol/parser.rb +148 -0
  51. data/lib/bitcoin/protocol/tx.rb +205 -0
  52. data/lib/bitcoin/protocol/txin.rb +97 -0
  53. data/lib/bitcoin/protocol/txout.rb +73 -0
  54. data/lib/bitcoin/protocol/version.rb +70 -0
  55. data/lib/bitcoin/script.rb +634 -0
  56. data/lib/bitcoin/storage/dummy.rb +164 -0
  57. data/lib/bitcoin/storage/models.rb +133 -0
  58. data/lib/bitcoin/storage/sequel.rb +335 -0
  59. data/lib/bitcoin/storage/sequel_store/sequel_migrations.rb +84 -0
  60. data/lib/bitcoin/storage/storage.rb +243 -0
  61. data/lib/bitcoin/version.rb +3 -0
  62. data/lib/bitcoin/wallet/coinselector.rb +30 -0
  63. data/lib/bitcoin/wallet/keygenerator.rb +75 -0
  64. data/lib/bitcoin/wallet/keystore.rb +203 -0
  65. data/lib/bitcoin/wallet/txdp.rb +116 -0
  66. data/lib/bitcoin/wallet/wallet.rb +243 -0
  67. data/spec/bitcoin/bitcoin_spec.rb +472 -0
  68. data/spec/bitcoin/builder_spec.rb +90 -0
  69. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +27 -0
  70. data/spec/bitcoin/fixtures/23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63.json +23 -0
  71. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +27 -0
  72. data/spec/bitcoin/fixtures/60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1.json +45 -0
  73. data/spec/bitcoin/fixtures/bc179baab547b7d7c1d5d8d6f8b0cc6318eaa4b0dd0a093ad6ac7f5a1cb6b3ba.json +34 -0
  74. data/spec/bitcoin/fixtures/rawblock-0.bin +0 -0
  75. data/spec/bitcoin/fixtures/rawblock-0.json +39 -0
  76. data/spec/bitcoin/fixtures/rawblock-1.bin +0 -0
  77. data/spec/bitcoin/fixtures/rawblock-1.json +39 -0
  78. data/spec/bitcoin/fixtures/rawblock-131025.bin +0 -0
  79. data/spec/bitcoin/fixtures/rawblock-131025.json +5063 -0
  80. data/spec/bitcoin/fixtures/rawblock-170.bin +0 -0
  81. data/spec/bitcoin/fixtures/rawblock-170.json +68 -0
  82. data/spec/bitcoin/fixtures/rawblock-9.bin +0 -0
  83. data/spec/bitcoin/fixtures/rawblock-9.json +39 -0
  84. data/spec/bitcoin/fixtures/rawblock-testnet-26478.bin +0 -0
  85. data/spec/bitcoin/fixtures/rawblock-testnet-26478.json +64 -0
  86. data/spec/bitcoin/fixtures/rawtx-01.bin +0 -0
  87. data/spec/bitcoin/fixtures/rawtx-01.json +27 -0
  88. data/spec/bitcoin/fixtures/rawtx-02.bin +0 -0
  89. data/spec/bitcoin/fixtures/rawtx-02.json +27 -0
  90. data/spec/bitcoin/fixtures/rawtx-03.bin +0 -0
  91. data/spec/bitcoin/fixtures/rawtx-03.json +48 -0
  92. data/spec/bitcoin/fixtures/rawtx-04.json +27 -0
  93. data/spec/bitcoin/fixtures/rawtx-0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9.bin +0 -0
  94. data/spec/bitcoin/fixtures/rawtx-05.json +23 -0
  95. data/spec/bitcoin/fixtures/rawtx-14be6fff8c6014f7c9493b4a6e4a741699173f39d74431b6b844fcb41ebb9984.bin +0 -0
  96. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.bin +0 -0
  97. data/spec/bitcoin/fixtures/rawtx-2f4a2717ec8c9f077a87dde6cbe0274d5238793a3f3f492b63c744837285e58a.json +27 -0
  98. data/spec/bitcoin/fixtures/rawtx-406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602.json +23 -0
  99. data/spec/bitcoin/fixtures/rawtx-52250a162c7d03d2e1fbc5ebd1801a88612463314b55102171c5b5d817d2d7b2.bin +0 -0
  100. data/spec/bitcoin/fixtures/rawtx-b5d4e8883533f99e5903ea2cf001a133a322fa6b1370b18a16c57c946a40823d.bin +0 -0
  101. data/spec/bitcoin/fixtures/rawtx-ba1ff5cd66713133c062a871a8adab92416f1e38d17786b2bf56ac5f6ffdfdf5.json +37 -0
  102. data/spec/bitcoin/fixtures/rawtx-c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73.json +24 -0
  103. data/spec/bitcoin/fixtures/rawtx-de35d060663750b3975b7997bde7fb76307cec5b270d12fcd9c4ad98b279c28c.json +23 -0
  104. data/spec/bitcoin/fixtures/rawtx-f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16.bin +0 -0
  105. data/spec/bitcoin/fixtures/rawtx-testnet-a220adf1902c46a39db25a24bc4178b6a88440f977a7e2cabfdd8b5c1dd35cfb.json +27 -0
  106. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.bin +0 -0
  107. data/spec/bitcoin/fixtures/rawtx-testnet-e232e0055dbdca88bbaa79458683195a0b7c17c5b6c524a8d146721d4d4d652f.json +41 -0
  108. data/spec/bitcoin/fixtures/reorg/blk_0_to_4.dat +0 -0
  109. data/spec/bitcoin/fixtures/reorg/blk_3A.dat +0 -0
  110. data/spec/bitcoin/fixtures/reorg/blk_4A.dat +0 -0
  111. data/spec/bitcoin/fixtures/reorg/blk_5A.dat +0 -0
  112. data/spec/bitcoin/fixtures/testnet/block_0.bin +0 -0
  113. data/spec/bitcoin/fixtures/testnet/block_1.bin +0 -0
  114. data/spec/bitcoin/fixtures/testnet/block_2.bin +0 -0
  115. data/spec/bitcoin/fixtures/testnet/block_3.bin +0 -0
  116. data/spec/bitcoin/fixtures/testnet/block_4.bin +0 -0
  117. data/spec/bitcoin/fixtures/testnet/block_5.bin +0 -0
  118. data/spec/bitcoin/fixtures/txdp-1.txt +32 -0
  119. data/spec/bitcoin/fixtures/txdp-2-signed.txt +19 -0
  120. data/spec/bitcoin/fixtures/txdp-2-unsigned.txt +14 -0
  121. data/spec/bitcoin/key_spec.rb +123 -0
  122. data/spec/bitcoin/network_spec.rb +48 -0
  123. data/spec/bitcoin/protocol/addr_spec.rb +68 -0
  124. data/spec/bitcoin/protocol/alert_spec.rb +20 -0
  125. data/spec/bitcoin/protocol/block_spec.rb +101 -0
  126. data/spec/bitcoin/protocol/inv_spec.rb +124 -0
  127. data/spec/bitcoin/protocol/ping_spec.rb +49 -0
  128. data/spec/bitcoin/protocol/tx_spec.rb +226 -0
  129. data/spec/bitcoin/protocol/version_spec.rb +77 -0
  130. data/spec/bitcoin/reorg_spec.rb +129 -0
  131. data/spec/bitcoin/script/opcodes_spec.rb +417 -0
  132. data/spec/bitcoin/script/script_spec.rb +246 -0
  133. data/spec/bitcoin/spec_helper.rb +36 -0
  134. data/spec/bitcoin/storage_spec.rb +229 -0
  135. data/spec/bitcoin/wallet/coinselector_spec.rb +35 -0
  136. data/spec/bitcoin/wallet/keygenerator_spec.rb +64 -0
  137. data/spec/bitcoin/wallet/keystore_spec.rb +188 -0
  138. data/spec/bitcoin/wallet/txdp_spec.rb +74 -0
  139. data/spec/bitcoin/wallet/wallet_spec.rb +207 -0
  140. 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