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.
- checksums.yaml +4 -4
- data/lib/bitcoin.rb +933 -0
- data/lib/bitcoin/bloom_filter.rb +125 -0
- data/lib/bitcoin/builder.rb +494 -0
- data/lib/bitcoin/connection.rb +130 -0
- data/lib/bitcoin/contracthash.rb +76 -0
- data/lib/bitcoin/dogecoin.rb +97 -0
- data/lib/bitcoin/electrum/mnemonic.rb +162 -0
- data/lib/bitcoin/ext_key.rb +191 -0
- data/lib/bitcoin/ffi/bitcoinconsensus.rb +75 -0
- data/lib/bitcoin/ffi/openssl.rb +388 -0
- data/lib/bitcoin/ffi/secp256k1.rb +266 -0
- data/lib/bitcoin/key.rb +280 -0
- data/lib/bitcoin/litecoin.rb +83 -0
- data/lib/bitcoin/logger.rb +86 -0
- data/lib/bitcoin/protocol.rb +189 -0
- data/lib/bitcoin/protocol/address.rb +50 -0
- data/lib/bitcoin/protocol/alert.rb +46 -0
- data/lib/bitcoin/protocol/aux_pow.rb +123 -0
- data/lib/bitcoin/protocol/block.rb +285 -0
- data/lib/bitcoin/protocol/handler.rb +43 -0
- data/lib/bitcoin/protocol/parser.rb +194 -0
- data/lib/bitcoin/protocol/partial_merkle_tree.rb +61 -0
- data/lib/bitcoin/protocol/reject.rb +38 -0
- data/lib/bitcoin/protocol/script_witness.rb +31 -0
- data/lib/bitcoin/protocol/tx.rb +587 -0
- data/lib/bitcoin/protocol/txin.rb +142 -0
- data/lib/bitcoin/protocol/txout.rb +95 -0
- data/lib/bitcoin/protocol/version.rb +88 -0
- data/lib/bitcoin/script.rb +1656 -0
- data/lib/bitcoin/trezor/mnemonic.rb +130 -0
- data/lib/bitcoin/version.rb +3 -0
- metadata +32 -1
@@ -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
|