monacoin-ruby 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ # See https://en.bitcoin.it/wiki/Merged_mining_specification.
8
+ #
9
+ # The AuxPow contains all data needed to verify that the child
10
+ # block was included in the parents coinbase transaction, and
11
+ # the parent satisfies the difficulty target.
12
+ #
13
+ # It encodes the +parent_block+ header, and its +coinbase_tx+.
14
+ # The +coinbase_branch+ and +coinbase_index+ can be used to recalculate
15
+ # the parent blocks merkle root and prove the coinbase transaction is
16
+ # really included in it.
17
+ # The +chain_branch+ and +chain_index+ are used to link the child block
18
+ # to the merkle root contained in the +coinbase_tx+. (So there can be
19
+ # more than one merge-mined chain)
20
+ #
21
+ # TODO: decode merged-mining data from +coinbase_tx+
22
+ class AuxPow
23
+
24
+ # Coinbase transaction of the parent block, linking to the child block
25
+ attr_accessor :coinbase_tx
26
+
27
+ # Hash of the parent block header
28
+ attr_accessor :block_hash
29
+
30
+ # Merkle branch linking the +coinbase_tx+ to the +parent_block+
31
+ attr_accessor :coinbase_branch
32
+
33
+ # Index of the +coinbase_tx+ in the parent blocks merkle tree
34
+ attr_accessor :coinbase_index
35
+
36
+ # Merkle branch linking the child block to the +coinbase_tx+
37
+ attr_accessor :chain_branch
38
+
39
+ # Index of the child block in the chain merkle tree
40
+ attr_accessor :chain_index
41
+
42
+ # Parent block header
43
+ attr_accessor :parent_block
44
+
45
+ def initialize(data)
46
+ parse_data (data) if data
47
+ end
48
+
49
+ def parse_data(data)
50
+ buf = StringIO.new(data)
51
+ parse_data_from_io(buf)
52
+ buf.eof? ? '' : buf.read
53
+ end
54
+
55
+ def parse_data_from_io(data)
56
+ @coinbase_tx = P::Tx.new(nil)
57
+ @coinbase_tx.parse_data_from_io(data)
58
+
59
+ @block_hash = data.read(32)
60
+ coinbase_branch_count = P.unpack_var_int_from_io(data)
61
+ @coinbase_branch = []
62
+ coinbase_branch_count.times{
63
+ break if data.eof?
64
+ @coinbase_branch << data.read(32).reverse.hth
65
+ }
66
+ @coinbase_index = data.read(4).unpack("I")[0]
67
+
68
+ @chain_branch = []
69
+ chain_branch_count = P.unpack_var_int_from_io(data)
70
+ chain_branch_count.times{
71
+ break if data.eof?
72
+ @chain_branch << data.read(32).reverse.hth
73
+ }
74
+
75
+ @chain_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 = @coinbase_tx.to_payload
85
+ payload << @block_hash
86
+ payload << P.pack_var_int(@coinbase_branch.count)
87
+ payload << @coinbase_branch.map(&:htb).map(&:reverse).join
88
+ payload << [@coinbase_index].pack("I")
89
+ payload << P.pack_var_int(@chain_branch.count)
90
+ payload << @chain_branch.map(&:htb).map(&:reverse).join
91
+ payload << [@chain_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
+ @coinbase_tx = P::Tx.from_hash(h['coinbase_tx'])
100
+ @block_hash = h['block_hash'].htb
101
+ @coinbase_branch = h['coinbase_branch']
102
+ @coinbase_index = h['coinbase_index']
103
+ @chain_branch = h['chain_branch']
104
+ @chain_index = h['chain_index']
105
+ @parent_block = P::Block.from_hash(h['parent_block'])
106
+ end
107
+ aux_pow
108
+ end
109
+
110
+ def to_hash
111
+ { 'coinbase_tx' => @coinbase_tx.to_hash,
112
+ 'block_hash' => @block_hash.hth,
113
+ 'coinbase_branch' => @coinbase_branch,
114
+ 'coinbase_index' => @coinbase_index,
115
+ 'chain_branch' => @chain_branch,
116
+ 'chain_index' => @chain_index,
117
+ 'parent_block' => @parent_block.to_hash }
118
+ end
119
+
120
+ end
121
+
122
+ end
123
+ end
@@ -0,0 +1,285 @@
1
+ # encoding: ascii-8bit
2
+
3
+ module Bitcoin
4
+ module Protocol
5
+
6
+ class Block
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
+
13
+ # block hash
14
+ attr_accessor :hash
15
+
16
+ # previous block hash
17
+ attr_accessor :prev_block_hash
18
+ alias :prev_block :prev_block_hash
19
+ def prev_block=(hash); @prev_block_hash = hash; end
20
+
21
+ # transactions (Array of Tx)
22
+ attr_accessor :tx
23
+
24
+ # merkle root
25
+ attr_accessor :mrkl_root
26
+
27
+ # block generation time
28
+ attr_accessor :time
29
+
30
+ # difficulty target bits
31
+ attr_accessor :bits
32
+
33
+ # nonce (number counted when searching for block hash matching target)
34
+ attr_accessor :nonce
35
+
36
+ # version (usually 1)
37
+ attr_accessor :ver
38
+
39
+ # raw protocol payload
40
+ attr_accessor :payload
41
+
42
+ # AuxPow linking the block to a merge-mined chain
43
+ attr_accessor :aux_pow
44
+
45
+ attr_reader :partial_merkle_tree
46
+
47
+ alias :transactions :tx
48
+
49
+ # compare to another block
50
+ def ==(other)
51
+ @hash == other.hash
52
+ end
53
+
54
+ def binary_hash
55
+ [@hash].pack("H*")
56
+ end
57
+
58
+ def prev_block_hex
59
+ @prev_block_hex ||= @prev_block_hash.reverse.unpack("H*")[0]
60
+ end
61
+
62
+ # create block from raw binary +data+
63
+ def initialize(data=nil)
64
+ @tx = []
65
+ parse_data_from_io(data) if data
66
+ end
67
+
68
+ # parse raw binary data
69
+ def parse_data(data)
70
+ buf = parse_data_from_io(data)
71
+ buf.eof? ? true : buf.read
72
+ end
73
+
74
+ # parse raw binary data
75
+ def parse_data_from_io(buf, header_only=false)
76
+ buf = buf.is_a?(String) ? StringIO.new(buf) : buf
77
+ @ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce = buf.read(80).unpack("Va32a32VVV")
78
+ recalc_block_hash
79
+
80
+ if Bitcoin.network[:auxpow_chain_id] != nil && (@ver & BLOCK_VERSION_AUXPOW) > 0
81
+ @aux_pow = AuxPow.new(nil)
82
+ @aux_pow.parse_data_from_io(buf)
83
+ end
84
+
85
+ return buf if buf.eof?
86
+
87
+ if header_only == :filtered
88
+ @tx_count = buf.read(4).unpack("V")[0]
89
+
90
+ nhashes = Protocol.unpack_var_int_from_io(buf)
91
+ hashes = []
92
+ nhashes.times do
93
+ hashes << buf.read(256 / 8)
94
+ end
95
+
96
+ nflags = Protocol.unpack_var_int_from_io(buf)
97
+ flags = buf.read(nflags)
98
+
99
+ @partial_merkle_tree = PartialMerkleTree.new(@tx_count, hashes, flags)
100
+ @partial_merkle_tree.set_value
101
+
102
+ return buf
103
+ end
104
+
105
+ tx_size = Protocol.unpack_var_int_from_io(buf)
106
+ @tx_count = tx_size
107
+ return buf if header_only
108
+
109
+ tx_size.times{
110
+ break if payload == true
111
+ return buf if buf.eof?
112
+
113
+ t = Tx.new(nil)
114
+ payload = t.parse_data_from_io(buf)
115
+ @tx << t
116
+ }
117
+
118
+ @payload = to_payload
119
+ buf
120
+ end
121
+
122
+ # recalculate the block hash
123
+ def recalc_block_hash
124
+ @hash = Bitcoin.block_hash(@prev_block_hash.reverse_hth, @mrkl_root.reverse_hth, @time, @bits, @nonce, @ver)
125
+ end
126
+
127
+ def recalc_block_scrypt_hash
128
+ @scrypt_hash = Bitcoin.block_scrypt_hash(@prev_block_hash.reverse_hth, @mrkl_root.reverse_hth, @time, @bits, @nonce, @ver)
129
+ end
130
+
131
+ def recalc_mrkl_root
132
+ @mrkl_root = if partial_merkle_tree
133
+ partial_merkle_tree.root.value.htb_reverse
134
+ else
135
+ Bitcoin.hash_mrkl_tree( @tx.map(&:hash) ).last.htb_reverse
136
+ end
137
+ end
138
+
139
+ # verify mrkl tree
140
+ def verify_mrkl_root
141
+ if partial_merkle_tree
142
+ partial_merkle_tree.valid_tree?(@mrkl_root.reverse_hth)
143
+ else
144
+ @mrkl_root.reverse_hth == Bitcoin.hash_mrkl_tree( @tx.map(&:hash) ).last
145
+ end
146
+ end
147
+
148
+ def tx_hashes
149
+ if partial_merkle_tree
150
+ partial_merkle_tree.tx_hashes
151
+ else
152
+ @tx.map(&:hash)
153
+ end
154
+ end
155
+
156
+ # get the block header info
157
+ # [<version>, <prev_block>, <merkle_root>, <time>, <bits>, <nonce>, <txcount>, <size>]
158
+ def header_info
159
+ [@ver, @prev_block_hash.reverse_hth, @mrkl_root.reverse_hth, Time.at(@time), @bits, @nonce, @tx.size, @payload.size]
160
+ end
161
+
162
+ # convert to raw binary format
163
+ def to_payload
164
+ head = [@ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce].pack("Va32a32VVV")
165
+ head << @aux_pow.to_payload if @aux_pow
166
+ return head if @tx.size == 0
167
+ head << Protocol.pack_var_int(@tx.size)
168
+ @tx.each{|tx| head << tx.to_payload }
169
+ head
170
+ end
171
+
172
+ # convert to ruby hash (see also #from_hash)
173
+ def to_hash(options = {})
174
+ h = {
175
+ 'hash' => @hash, 'ver' => @ver,
176
+ 'prev_block' => @prev_block_hash.reverse_hth, 'mrkl_root' => @mrkl_root.reverse_hth,
177
+ 'time' => @time, 'bits' => @bits, 'nonce' => @nonce,
178
+ 'n_tx' => @tx.size, 'size' => (@payload||to_payload).bytesize,
179
+ 'tx' => @tx.map{|i| i.to_hash(options) },
180
+ 'mrkl_tree' => Bitcoin.hash_mrkl_tree( @tx.map{|i| i.hash } )
181
+ }
182
+ h['aux_pow'] = @aux_pow.to_hash if @aux_pow
183
+ h
184
+ end
185
+
186
+ def size
187
+ payload.bytesize
188
+ end
189
+
190
+ def hextarget
191
+ Bitcoin.decode_compact_bits(@bits)
192
+ end
193
+
194
+ def decimaltarget
195
+ Bitcoin.decode_compact_bits(@bits).to_i(16)
196
+ end
197
+
198
+ def difficulty
199
+ Bitcoin.block_difficulty(@bits)
200
+ end
201
+
202
+ # introduced in block version 2 by BIP_0034
203
+ # blockchain height as seen by the block itself.
204
+ # do not trust this value, instead verify with chain storage.
205
+ def bip34_block_height(height=nil)
206
+ return nil unless @ver >= 2
207
+ if height # generate height binary
208
+ buf = [height].pack("V").gsub(/\x00+$/,"")
209
+ [buf.bytesize, buf].pack("Ca*")
210
+ else
211
+ coinbase = @tx.first.inputs.first.script_sig
212
+ coinbase[1..coinbase[0].ord].ljust(4, "\x00").unpack("V").first
213
+ end
214
+ rescue
215
+ nil
216
+ end
217
+
218
+ # convert to json representation as seen in the block explorer.
219
+ # (see also #from_json)
220
+ def to_json(options = {:space => ''}, *a)
221
+ JSON.pretty_generate( to_hash(options), options )
222
+ end
223
+
224
+ # write json representation to a file
225
+ # (see also #to_json)
226
+ def to_json_file(path)
227
+ File.open(path, 'wb'){|f| f.print to_json; }
228
+ end
229
+
230
+ # parse ruby hash (see also #to_hash)
231
+ def self.from_hash(h, do_raise=true)
232
+ blk = new(nil)
233
+ blk.instance_eval{
234
+ @ver, @time, @bits, @nonce = h.values_at('ver', 'time', 'bits', 'nonce')
235
+ @prev_block_hash, @mrkl_root = h.values_at('prev_block', 'mrkl_root').map{|i| i.htb_reverse }
236
+ unless h['hash'] == recalc_block_hash
237
+ raise "Block hash mismatch! Claimed: #{h['hash']}, Actual: #{@hash}" if do_raise
238
+ end
239
+ @aux_pow = AuxPow.from_hash(h['aux_pow']) if h['aux_pow']
240
+ h['tx'].each{|tx| @tx << Tx.from_hash(tx, do_raise) }
241
+ if h['tx'].any?
242
+ (raise "Block merkle root mismatch! Block: #{h['hash']}" unless verify_mrkl_root) if do_raise
243
+ end
244
+ }
245
+ blk
246
+ end
247
+
248
+ # convert ruby hash to raw binary
249
+ def self.binary_from_hash(h); from_hash(h).to_payload; end
250
+
251
+ # parse json representation (see also #to_json)
252
+ def self.from_json(json_string); from_hash( JSON.load(json_string) ); end
253
+
254
+ # convert json representation to raw binary
255
+ def self.binary_from_json(json_string); from_json(json_string).to_payload; end
256
+
257
+ # convert header to json representation.
258
+ def header_to_json(options = {:space => ''})
259
+ h = to_hash
260
+ %w[tx mrkl_tree].each{|k| h.delete(k) }
261
+ JSON.pretty_generate( h, options )
262
+ end
263
+
264
+ # block header binary output
265
+ def block_header
266
+ [@ver, @prev_block_hash, @mrkl_root, @time, @bits, @nonce, Protocol.pack_var_int(0)].pack("Va32a32VVVa*")
267
+ end
268
+
269
+ # read binary block from a file
270
+ def self.from_file(path); new( Bitcoin::Protocol.read_binary_file(path) ); end
271
+
272
+ # read json block from a file
273
+ def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
274
+
275
+ # get the (statistical) amount of work that was needed to generate this block.
276
+ def block_work
277
+ target = Bitcoin.decode_compact_bits(@bits).to_i(16)
278
+ return 0 if target <= 0
279
+ (2**256) / (target + 1)
280
+ end
281
+
282
+ end
283
+
284
+ end
285
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: ascii-8bit
2
+
3
+ module Bitcoin
4
+ module Protocol
5
+
6
+ class Handler
7
+ def on_inv_transaction(hash)
8
+ p ['inv transaction', hash.hth]
9
+ end
10
+
11
+ def on_inv_block(hash)
12
+ p ['inv block', hash.hth]
13
+ end
14
+
15
+ def on_get_transaction(hash)
16
+ p ['get transaction', hash.hth]
17
+ end
18
+
19
+ def on_get_block(hash)
20
+ p ['get block', hash.hth]
21
+ end
22
+
23
+ def on_addr(addr)
24
+ p ['addr', addr, addr.alive?]
25
+ end
26
+
27
+ def on_tx(tx)
28
+ p ['tx', tx]
29
+ end
30
+
31
+ def on_block(block)
32
+ #p ['block', block]
33
+ puts block.to_json
34
+ end
35
+
36
+ def on_error(message, payload)
37
+ p ['error', message, payload]
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+ end