monacoin-ruby 0.1.2 → 0.1.3

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.
@@ -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