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