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,194 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
module Bitcoin
|
4
|
+
module Protocol
|
5
|
+
|
6
|
+
class Parser
|
7
|
+
attr_reader :stats
|
8
|
+
|
9
|
+
def initialize(handler=nil)
|
10
|
+
@h = handler || Handler.new
|
11
|
+
@buf = ""
|
12
|
+
@stats = { 'total_packets' => 0, 'total_bytes' => 0, 'total_errors' => 0 }
|
13
|
+
end
|
14
|
+
|
15
|
+
def log; @log ||= Bitcoin::Logger.create("parser"); end
|
16
|
+
|
17
|
+
# handles inv/getdata packets
|
18
|
+
def parse_inv(payload, type=:put)
|
19
|
+
count, payload = Protocol.unpack_var_int(payload)
|
20
|
+
payload.each_byte.each_slice(36).with_index do |i, idx|
|
21
|
+
hash = i[4..-1].reverse.pack("C32")
|
22
|
+
case i[0]
|
23
|
+
when 1
|
24
|
+
type == :put ? @h.on_inv_transaction(hash) : @h.on_get_transaction(hash)
|
25
|
+
when 2
|
26
|
+
if type == :put
|
27
|
+
if @h.respond_to?(:on_inv_block_v2)
|
28
|
+
@h.on_inv_block_v2(hash, idx, count)
|
29
|
+
else
|
30
|
+
@h.on_inv_block(hash)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
@h.on_get_block(hash)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
parse_error :parse_inv, i.pack("C*")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_addr(payload)
|
42
|
+
count, payload = Protocol.unpack_var_int(payload)
|
43
|
+
payload.each_byte.each_slice(30) do |i|
|
44
|
+
@h.on_addr(Addr.new(i.pack("C*"))) rescue parse_error(:addr, i.pack("C*"))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_headers(payload)
|
49
|
+
return unless @h.respond_to?(:on_headers)
|
50
|
+
buf = StringIO.new(payload)
|
51
|
+
count = Protocol.unpack_var_int_from_io(buf)
|
52
|
+
headers = count.times.map{
|
53
|
+
break if buf.eof?
|
54
|
+
b = Block.new; b.parse_data_from_io(buf, header_only=true); b
|
55
|
+
}
|
56
|
+
@h.on_headers(headers)
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_mrkle_block(payload)
|
60
|
+
return unless @h.respond_to?(:on_mrkle_block)
|
61
|
+
b = Block.new
|
62
|
+
b.parse_data_from_io(payload, header_only= :filtered)
|
63
|
+
@h.on_mrkle_block(b)
|
64
|
+
end
|
65
|
+
|
66
|
+
def parse_getblocks(payload)
|
67
|
+
version, payload = payload.unpack('Va*')
|
68
|
+
count, payload = Protocol.unpack_var_int(payload)
|
69
|
+
buf, payload = payload.unpack("a#{count*32}a*")
|
70
|
+
hashes = buf.each_byte.each_slice(32).map{|i| hash = i.reverse.pack("C32").hth }
|
71
|
+
stop_hash = payload[0..32].reverse_hth
|
72
|
+
[version, hashes, stop_hash]
|
73
|
+
end
|
74
|
+
|
75
|
+
def process_pkt(command, payload)
|
76
|
+
@stats['total_packets'] += 1
|
77
|
+
@stats['total_bytes'] += payload.bytesize
|
78
|
+
@stats[command] ? (@stats[command] += 1) : @stats[command] = 1
|
79
|
+
case command
|
80
|
+
when 'tx'; @h.on_tx( Tx.new(payload) )
|
81
|
+
when 'block'; @h.on_block( Block.new(payload) )
|
82
|
+
when 'headers'; parse_headers(payload)
|
83
|
+
when 'inv'; parse_inv(payload, :put)
|
84
|
+
when 'getdata'; parse_inv(payload, :get)
|
85
|
+
when 'addr'; parse_addr(payload)
|
86
|
+
when 'getaddr'; @h.on_getaddr if @h.respond_to?(:on_getaddr)
|
87
|
+
when 'verack'; @h.respond_to?(:on_verack) ? @h.on_verack : (@h.respond_to?(:on_handshake_complete) ? @h.on_handshake_complete : nil)
|
88
|
+
when 'version'; parse_version(payload)
|
89
|
+
when 'alert'; parse_alert(payload)
|
90
|
+
when 'ping'; @h.on_ping(payload.unpack("Q")[0])
|
91
|
+
when 'pong'; @h.on_pong(payload.unpack("Q")[0])
|
92
|
+
when 'getblocks'; @h.on_getblocks(*parse_getblocks(payload)) if @h.respond_to?(:on_getblocks)
|
93
|
+
when 'getheaders'; @h.on_getheaders(*parse_getblocks(payload)) if @h.respond_to?(:on_getheaders)
|
94
|
+
when 'mempool'; handle_mempool_request(payload)
|
95
|
+
when 'notfound'; handle_notfound_reply(payload)
|
96
|
+
when 'merkleblock'; parse_mrkle_block(payload)
|
97
|
+
when 'reject'; handle_reject(payload)
|
98
|
+
else
|
99
|
+
parse_error :unknown_packet, [command, payload.hth]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def parse_version(payload)
|
104
|
+
@version = Bitcoin::Protocol::Version.parse(payload)
|
105
|
+
@h.on_version(@version)
|
106
|
+
end
|
107
|
+
|
108
|
+
def parse_alert(payload)
|
109
|
+
return unless @h.respond_to?(:on_alert)
|
110
|
+
@h.on_alert Bitcoin::Protocol::Alert.parse(payload)
|
111
|
+
end
|
112
|
+
|
113
|
+
def handle_reject(payload)
|
114
|
+
return unless @h.respond_to?(:on_reject)
|
115
|
+
@h.on_reject Bitcoin::Protocol::Reject.parse(payload)
|
116
|
+
end
|
117
|
+
|
118
|
+
# https://en.bitcoin.it/wiki/BIP_0035
|
119
|
+
def handle_mempool_request(payload)
|
120
|
+
return unless @version.fields[:version] >= 60002 # Protocol version >= 60002
|
121
|
+
return unless (@version.fields[:services] & Bitcoin::Protocol::Version::NODE_NETWORK) == 1 # NODE_NETWORK bit set in Services
|
122
|
+
@h.on_mempool if @h.respond_to?(:on_mempool)
|
123
|
+
end
|
124
|
+
|
125
|
+
def handle_notfound_reply(payload)
|
126
|
+
return unless @h.respond_to?(:on_notfound)
|
127
|
+
count, payload = Protocol.unpack_var_int(payload)
|
128
|
+
payload.each_byte.each_slice(36) do |i|
|
129
|
+
hash = i[4..-1].reverse.pack("C32")
|
130
|
+
case i[0]
|
131
|
+
when 1; @h.on_notfound(:tx, hash)
|
132
|
+
when 2; @h.on_notfound(:block, hash)
|
133
|
+
else
|
134
|
+
parse_error(:notfound, [i.pack("C*"), hash])
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def parse(buf)
|
140
|
+
@buf += buf
|
141
|
+
while parse_buffer; end
|
142
|
+
@buf
|
143
|
+
end
|
144
|
+
|
145
|
+
def parse_buffer
|
146
|
+
head_magic = Bitcoin::network[:magic_head]
|
147
|
+
head_size = 24
|
148
|
+
return false if @buf.size < head_size
|
149
|
+
|
150
|
+
magic, cmd, length, checksum = @buf.unpack("a4A12Va4")
|
151
|
+
payload = @buf[head_size...head_size+length]
|
152
|
+
|
153
|
+
unless magic == head_magic
|
154
|
+
handle_stream_error(:close, "head_magic not found")
|
155
|
+
@buf = ''
|
156
|
+
else
|
157
|
+
|
158
|
+
if Digest::SHA256.digest(Digest::SHA256.digest( payload ))[0...4] != checksum
|
159
|
+
if (length < 50000) && (payload.size < length)
|
160
|
+
size_info = [payload.size, length].join('/')
|
161
|
+
handle_stream_error(:debug, "chunked packet stream (#{size_info})")
|
162
|
+
else
|
163
|
+
handle_stream_error(:close, "checksum mismatch")
|
164
|
+
end
|
165
|
+
return
|
166
|
+
end
|
167
|
+
@buf = @buf[head_size+length..-1] || ""
|
168
|
+
|
169
|
+
process_pkt(cmd, payload)
|
170
|
+
end
|
171
|
+
|
172
|
+
# not empty yet? parse more.
|
173
|
+
@buf[0] != nil
|
174
|
+
end
|
175
|
+
|
176
|
+
def handle_stream_error(type, msg)
|
177
|
+
case type
|
178
|
+
when :close
|
179
|
+
log.debug {"closing packet stream (#{msg})"}
|
180
|
+
else
|
181
|
+
log.debug { [type, msg] }
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def parse_error *err
|
186
|
+
@stats['total_errors'] += 1
|
187
|
+
return unless @h.respond_to?(:on_error)
|
188
|
+
@h.on_error *err
|
189
|
+
end
|
190
|
+
|
191
|
+
end # Parser
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class Bitcoin::Protocol::PartialMerkleTree
|
2
|
+
Node = Struct.new(:value, :left, :right, :width_idx)
|
3
|
+
|
4
|
+
BIT_MASK = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]
|
5
|
+
|
6
|
+
def initialize(total_txs, hashes, flags)
|
7
|
+
@total_txs, @flags = total_txs, flags
|
8
|
+
@hashes = hashes.map{|h| h.reverse_hth }
|
9
|
+
@visit_idx = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def tx_hashes
|
13
|
+
@leaves.reject{|n| n.value.nil? }.map{|n| n.value }
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_tree
|
17
|
+
lay = @leaves = @total_txs.times.map{ Node.new(nil, nil, nil) }
|
18
|
+
while lay.size > 1
|
19
|
+
lay = lay.each_slice(2).map do |left, right|
|
20
|
+
Node.new(nil, left, right)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
return lay[0]
|
24
|
+
end
|
25
|
+
|
26
|
+
def current_flag
|
27
|
+
@flags[@visit_idx / 8].ord & BIT_MASK[@visit_idx % 8] == 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def root
|
31
|
+
@root ||= build_tree
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_value(node = root)
|
35
|
+
if current_flag || (node.left.nil? && node.right.nil?)
|
36
|
+
node.value = @hashes.shift
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
40
|
+
if node.left
|
41
|
+
@visit_idx += 1
|
42
|
+
set_value(node.left)
|
43
|
+
end
|
44
|
+
if node.right
|
45
|
+
@visit_idx += 1
|
46
|
+
set_value(node.right)
|
47
|
+
end
|
48
|
+
|
49
|
+
right = node.right || node.left
|
50
|
+
node.value = Bitcoin.bitcoin_mrkl(node.left.value, right.value)
|
51
|
+
|
52
|
+
return
|
53
|
+
end
|
54
|
+
|
55
|
+
def valid_tree?(mrkl_root_hash)
|
56
|
+
return false unless @hashes.empty?
|
57
|
+
return false if ((@visit_idx + 1)/8.0).ceil != @flags.length
|
58
|
+
return false if mrkl_root_hash != root.value
|
59
|
+
return true
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
module Bitcoin
|
4
|
+
module Protocol
|
5
|
+
|
6
|
+
class Reject < Struct.new(:message, :ccode, :reason, :data)
|
7
|
+
CCODE_TABLE = {
|
8
|
+
0x01 => :malformed,
|
9
|
+
0x10 => :invalid,
|
10
|
+
0x11 => :obsolete,
|
11
|
+
0x12 => :duplicate,
|
12
|
+
0x40 => :nonstandard,
|
13
|
+
0x41 => :dust,
|
14
|
+
0x42 => :insufficientfee,
|
15
|
+
0x43 => :checkpoint,
|
16
|
+
}
|
17
|
+
|
18
|
+
def self.parse(payload)
|
19
|
+
message, payload = Bitcoin::Protocol.unpack_var_string(payload)
|
20
|
+
ccode, payload = payload.unpack("Ca*")
|
21
|
+
reason, payload = Bitcoin::Protocol.unpack_var_string(payload)
|
22
|
+
data = payload
|
23
|
+
|
24
|
+
code = CCODE_TABLE[ccode] || ccode
|
25
|
+
new(message, code, reason, data)
|
26
|
+
end
|
27
|
+
|
28
|
+
def tx_hash
|
29
|
+
message == "tx" && self[:data].reverse.bth
|
30
|
+
end
|
31
|
+
|
32
|
+
def block_hash
|
33
|
+
message == "block" && self[:data].reverse.bth
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
module Bitcoin
|
4
|
+
|
5
|
+
module Protocol
|
6
|
+
|
7
|
+
class ScriptWitness
|
8
|
+
|
9
|
+
# witness stack
|
10
|
+
attr_reader :stack
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@stack = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# check empty
|
17
|
+
def empty?
|
18
|
+
stack.empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
# output script in raw binary format
|
22
|
+
def to_payload
|
23
|
+
payload = Bitcoin::Protocol.pack_var_int(stack.size)
|
24
|
+
payload << stack.map{|e| Bitcoin::Protocol.pack_var_int(e.bytesize) << e }.join
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,587 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
require 'bitcoin/script'
|
4
|
+
|
5
|
+
module Bitcoin
|
6
|
+
module Protocol
|
7
|
+
|
8
|
+
class Tx
|
9
|
+
|
10
|
+
MARKER = 0
|
11
|
+
FLAG = 1
|
12
|
+
|
13
|
+
SIGHASH_TYPE = Script::SIGHASH_TYPE
|
14
|
+
|
15
|
+
# transaction hash
|
16
|
+
attr_reader :hash
|
17
|
+
|
18
|
+
# inputs (Array of TxIn)
|
19
|
+
attr_reader :in
|
20
|
+
|
21
|
+
# outputs (Array of TxOut)
|
22
|
+
attr_reader :out
|
23
|
+
|
24
|
+
# raw protocol payload
|
25
|
+
attr_reader :payload
|
26
|
+
|
27
|
+
# version (usually 1)
|
28
|
+
attr_accessor :ver
|
29
|
+
|
30
|
+
# lock time
|
31
|
+
attr_accessor :lock_time
|
32
|
+
|
33
|
+
# parsed / evaluated input scripts cached for later use
|
34
|
+
attr_reader :scripts
|
35
|
+
|
36
|
+
attr_accessor :marker
|
37
|
+
attr_accessor :flag
|
38
|
+
|
39
|
+
alias :inputs :in
|
40
|
+
alias :outputs :out
|
41
|
+
|
42
|
+
# compare to another tx
|
43
|
+
def ==(other)
|
44
|
+
@hash == other.hash
|
45
|
+
end
|
46
|
+
|
47
|
+
# return the tx hash in binary format
|
48
|
+
def binary_hash
|
49
|
+
@binary_hash ||= [@hash].pack("H*").reverse
|
50
|
+
end
|
51
|
+
|
52
|
+
# create tx from raw binary +data+
|
53
|
+
def initialize(data=nil)
|
54
|
+
@ver, @lock_time, @in, @out, @scripts = 1, 0, [], [], []
|
55
|
+
@enable_bitcoinconsensus = !!ENV['USE_BITCOINCONSENSUS']
|
56
|
+
parse_data_from_io(data) if data
|
57
|
+
end
|
58
|
+
|
59
|
+
# generate the tx hash for given +payload+ in hex format
|
60
|
+
def hash_from_payload(payload)
|
61
|
+
Digest::SHA256.digest(Digest::SHA256.digest( payload )).reverse_hth
|
62
|
+
end
|
63
|
+
alias generate_hash hash_from_payload
|
64
|
+
|
65
|
+
# refresh_hash recalculates the tx hash and sets it on the instance
|
66
|
+
def refresh_hash
|
67
|
+
@hash = generate_hash(to_old_payload)
|
68
|
+
end
|
69
|
+
|
70
|
+
# add an input
|
71
|
+
def add_in(input); (@in ||= []) << input; end
|
72
|
+
|
73
|
+
# add an output
|
74
|
+
def add_out(output); (@out ||= []) << output; end
|
75
|
+
|
76
|
+
# parse raw binary data
|
77
|
+
def parse_data_from_io(data)
|
78
|
+
buf = data.is_a?(String) ? StringIO.new(data) : data
|
79
|
+
|
80
|
+
@ver = buf.read(4).unpack("V")[0]
|
81
|
+
|
82
|
+
return false if buf.eof?
|
83
|
+
|
84
|
+
# segwit serialization format is defined by https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki
|
85
|
+
# Also note that it is impossible to parse 0 input transactions. Regular transactions with 0 inputs look
|
86
|
+
# like malformed segwit transactions.
|
87
|
+
@marker = buf.read(1).unpack('c').first
|
88
|
+
@flag = buf.read(1).unpack('c').first
|
89
|
+
|
90
|
+
witness = @marker == 0 && @flag != 0
|
91
|
+
|
92
|
+
# Non-segwit format does not contain marker or flag fields.
|
93
|
+
buf.seek(buf.pos - 2) unless witness
|
94
|
+
|
95
|
+
in_size = Protocol.unpack_var_int_from_io(buf)
|
96
|
+
|
97
|
+
@in = []
|
98
|
+
in_size.times{
|
99
|
+
break if buf.eof?
|
100
|
+
@in << TxIn.from_io(buf)
|
101
|
+
}
|
102
|
+
|
103
|
+
return false if buf.eof?
|
104
|
+
|
105
|
+
out_size = Protocol.unpack_var_int_from_io(buf)
|
106
|
+
@out = []
|
107
|
+
out_size.times{
|
108
|
+
break if buf.eof?
|
109
|
+
@out << TxOut.from_io(buf)
|
110
|
+
}
|
111
|
+
|
112
|
+
return false if buf.eof?
|
113
|
+
|
114
|
+
if witness
|
115
|
+
in_size.times do |i|
|
116
|
+
witness_count = Protocol.unpack_var_int_from_io(buf)
|
117
|
+
witness_count.times do
|
118
|
+
size = Protocol.unpack_var_int_from_io(buf)
|
119
|
+
@in[i].script_witness.stack << buf.read(size)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
@lock_time = buf.read(4).unpack("V")[0]
|
125
|
+
|
126
|
+
@hash = hash_from_payload(to_old_payload)
|
127
|
+
@payload = to_payload
|
128
|
+
|
129
|
+
if buf.eof?
|
130
|
+
true
|
131
|
+
else
|
132
|
+
data.is_a?(StringIO) ? buf : buf.read
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
alias :parse_data :parse_data_from_io
|
137
|
+
|
138
|
+
# output transaction in raw binary format
|
139
|
+
def to_payload
|
140
|
+
witness? ? to_witness_payload : to_old_payload
|
141
|
+
end
|
142
|
+
|
143
|
+
def to_old_payload
|
144
|
+
pin = ""
|
145
|
+
@in.each{|input| pin << input.to_payload }
|
146
|
+
pout = ""
|
147
|
+
@out.each{|output| pout << output.to_payload }
|
148
|
+
|
149
|
+
[@ver].pack("V") << Protocol.pack_var_int(@in.size) << pin << Protocol.pack_var_int(@out.size) << pout << [@lock_time].pack("V")
|
150
|
+
end
|
151
|
+
|
152
|
+
# output transaction in raw binary format with witness
|
153
|
+
def to_witness_payload
|
154
|
+
buf = [@ver, MARKER, FLAG].pack('Vcc')
|
155
|
+
buf << Protocol.pack_var_int(@in.length) << @in.map(&:to_payload).join
|
156
|
+
buf << Protocol.pack_var_int(@out.length) << @out.map(&:to_payload).join
|
157
|
+
buf << witness_payload << [@lock_time].pack('V')
|
158
|
+
buf
|
159
|
+
end
|
160
|
+
|
161
|
+
def witness_payload
|
162
|
+
@in.map { |i| i.script_witness.to_payload }.join
|
163
|
+
end
|
164
|
+
|
165
|
+
def witness?
|
166
|
+
!@in.find { |i| !i.script_witness.empty? }.nil?
|
167
|
+
end
|
168
|
+
|
169
|
+
# generate a signature hash for input +input_idx+.
|
170
|
+
# either pass the +outpoint_tx+ or the +script_pubkey+ directly.
|
171
|
+
def signature_hash_for_input(input_idx, subscript, hash_type=nil, prev_out_value=nil, fork_id=nil)
|
172
|
+
# https://github.com/bitcoin/bitcoin/blob/e071a3f6c06f41068ad17134189a4ac3073ef76b/script.cpp#L834
|
173
|
+
# http://code.google.com/p/bitcoinj/source/browse/trunk/src/com/google/bitcoin/core/Script.java#318
|
174
|
+
# https://en.bitcoin.it/wiki/OP_CHECKSIG#How_it_works
|
175
|
+
# https://github.com/bitcoin/bitcoin/blob/c2e8c8acd8ae0c94c70b59f55169841ad195bb99/src/script.cpp#L1058
|
176
|
+
# https://en.bitcoin.it/wiki/OP_CHECKSIG
|
177
|
+
|
178
|
+
hash_type ||= SIGHASH_TYPE[:all]
|
179
|
+
|
180
|
+
# fork_id is optional and if set, SIGHASH_FORKID flag as defined by the
|
181
|
+
# Bitcoin Cash protocol will be respected.
|
182
|
+
#
|
183
|
+
# https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/doc/abc/replay-protected-sighash.md
|
184
|
+
if fork_id && (hash_type&SIGHASH_TYPE[:forkid]) != 0
|
185
|
+
raise "SIGHASH_FORKID is enabled, so prev_out_value is required" if prev_out_value.nil?
|
186
|
+
|
187
|
+
# According to the spec, we should modify the sighash by replacing the 24 most significant
|
188
|
+
# bits with the fork ID. However, Bitcoin ABC does not currently implement this since the
|
189
|
+
# fork_id is an implicit 0 and it would make the sighash JSON tests fail. Will leave as a
|
190
|
+
# TODO for now.
|
191
|
+
raise NotImplementedError, "fork_id must be 0" unless fork_id == 0
|
192
|
+
|
193
|
+
script_code = Bitcoin::Protocol.pack_var_string(subscript)
|
194
|
+
return signature_hash_for_input_bip143(input_idx, script_code, prev_out_value, hash_type)
|
195
|
+
end
|
196
|
+
|
197
|
+
# Note: BitcoinQT checks if input_idx >= @in.size and returns 1 with an error message.
|
198
|
+
# But this check is never actually useful because BitcoinQT would crash
|
199
|
+
# right before VerifyScript if input index is out of bounds (inside CScriptCheck::operator()()).
|
200
|
+
# That's why we don't need to do such a check here.
|
201
|
+
#
|
202
|
+
# However, if you look at the case SIGHASH_TYPE[:single] below, we must
|
203
|
+
# return 1 because it's possible to have more inputs than outputs and BitcoinQT returns 1 as well.
|
204
|
+
return "\x01".ljust(32, "\x00") if input_idx >= @in.size # ERROR: SignatureHash() : input_idx=%d out of range
|
205
|
+
|
206
|
+
pin = @in.map.with_index{|input,idx|
|
207
|
+
if idx == input_idx
|
208
|
+
subscript = subscript.out[ input.prev_out_index ].script if subscript.respond_to?(:out) # legacy api (outpoint_tx)
|
209
|
+
|
210
|
+
# Remove all instances of OP_CODESEPARATOR from the script.
|
211
|
+
parsed_subscript = Script.new(subscript)
|
212
|
+
parsed_subscript.chunks.delete(Script::OP_CODESEPARATOR)
|
213
|
+
subscript = parsed_subscript.to_binary
|
214
|
+
|
215
|
+
input.to_payload(subscript)
|
216
|
+
else
|
217
|
+
case (hash_type & 0x1f)
|
218
|
+
when SIGHASH_TYPE[:none]; input.to_payload("", "\x00\x00\x00\x00")
|
219
|
+
when SIGHASH_TYPE[:single]; input.to_payload("", "\x00\x00\x00\x00")
|
220
|
+
else; input.to_payload("")
|
221
|
+
end
|
222
|
+
end
|
223
|
+
}
|
224
|
+
|
225
|
+
pout = @out.map(&:to_payload)
|
226
|
+
in_size, out_size = Protocol.pack_var_int(@in.size), Protocol.pack_var_int(@out.size)
|
227
|
+
|
228
|
+
case (hash_type & 0x1f)
|
229
|
+
when SIGHASH_TYPE[:none]
|
230
|
+
pout = ""
|
231
|
+
out_size = Protocol.pack_var_int(0)
|
232
|
+
when SIGHASH_TYPE[:single]
|
233
|
+
return "\x01".ljust(32, "\x00") if input_idx >= @out.size # ERROR: SignatureHash() : input_idx=%d out of range (SIGHASH_SINGLE)
|
234
|
+
pout = @out[0...(input_idx+1)].map.with_index{|out,idx| (idx==input_idx) ? out.to_payload : out.to_null_payload }.join
|
235
|
+
out_size = Protocol.pack_var_int(input_idx+1)
|
236
|
+
end
|
237
|
+
|
238
|
+
if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
|
239
|
+
in_size, pin = Protocol.pack_var_int(1), [ pin[input_idx] ]
|
240
|
+
end
|
241
|
+
|
242
|
+
buf = [ [@ver].pack("V"), in_size, pin, out_size, pout, [@lock_time, hash_type].pack("VV") ].join
|
243
|
+
Digest::SHA256.digest( Digest::SHA256.digest( buf ) )
|
244
|
+
end
|
245
|
+
|
246
|
+
# generate a witness signature hash for input +input_idx+.
|
247
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
|
248
|
+
def signature_hash_for_witness_input(input_idx, witness_program, prev_out_value, witness_script = nil, hash_type=nil, skip_separator_index = 0)
|
249
|
+
return "\x01".ljust(32, "\x00") if input_idx >= @in.size # ERROR: SignatureHash() : input_idx=%d out of range
|
250
|
+
|
251
|
+
hash_type ||= SIGHASH_TYPE[:all]
|
252
|
+
|
253
|
+
script = Bitcoin::Script.new(witness_program)
|
254
|
+
raise "ScriptPubkey does not contain witness program." unless script.is_witness?
|
255
|
+
|
256
|
+
if script.is_witness_v0_keyhash?
|
257
|
+
script_code = [["1976a914", script.get_hash160, "88ac"].join].pack("H*")
|
258
|
+
elsif script.is_witness_v0_scripthash?
|
259
|
+
raise "witness script does not match script pubkey" unless Bitcoin::Script.to_witness_p2sh_script(Digest::SHA256.digest(witness_script).bth) == witness_program
|
260
|
+
script = skip_separator_index > 0 ? Bitcoin::Script.new(witness_script).subscript_codeseparator(skip_separator_index) : witness_script
|
261
|
+
script_code = Bitcoin::Protocol.pack_var_string(script)
|
262
|
+
end
|
263
|
+
|
264
|
+
signature_hash_for_input_bip143(input_idx, script_code, prev_out_value, hash_type)
|
265
|
+
end
|
266
|
+
|
267
|
+
# verify input signature +in_idx+ against the corresponding
|
268
|
+
# output in +outpoint_tx+
|
269
|
+
# outpoint. This arg can also be a Script or TxOut.
|
270
|
+
#
|
271
|
+
# options are: verify_sigpushonly, verify_minimaldata, verify_cleanstack, verify_dersig, verify_low_s, verify_strictenc, fork_id
|
272
|
+
def verify_input_signature(in_idx, outpoint_data, block_timestamp=Time.now.to_i, opts={})
|
273
|
+
if @enable_bitcoinconsensus
|
274
|
+
return bitcoinconsensus_verify_script(in_idx, outpoint_data, block_timestamp, opts)
|
275
|
+
end
|
276
|
+
|
277
|
+
# If FORKID is enabled, we also ensure strict encoding.
|
278
|
+
opts[:verify_strictenc] ||= !!opts[:fork_id]
|
279
|
+
|
280
|
+
outpoint_idx = @in[in_idx].prev_out_index
|
281
|
+
script_sig = @in[in_idx].script_sig
|
282
|
+
|
283
|
+
amount = amount_from_outpoint_data(outpoint_data, outpoint_idx)
|
284
|
+
script_pubkey = script_pubkey_from_outpoint_data(outpoint_data, outpoint_idx)
|
285
|
+
|
286
|
+
if opts[:fork_id] && amount.nil?
|
287
|
+
raise "verify_input_signature must be called with a previous transaction or " \
|
288
|
+
"transaction output if SIGHASH_FORKID is enabled"
|
289
|
+
end
|
290
|
+
|
291
|
+
@scripts[in_idx] = Bitcoin::Script.new(script_sig, script_pubkey)
|
292
|
+
return false if opts[:verify_sigpushonly] && !@scripts[in_idx].is_push_only?(script_sig)
|
293
|
+
return false if opts[:verify_minimaldata] && !@scripts[in_idx].pushes_are_canonical?
|
294
|
+
sig_valid = @scripts[in_idx].run(block_timestamp, opts) do |pubkey,sig,hash_type,subscript|
|
295
|
+
hash = signature_hash_for_input(in_idx, subscript, hash_type, amount, opts[:fork_id])
|
296
|
+
Bitcoin.verify_signature( hash, sig, pubkey.unpack("H*")[0] )
|
297
|
+
end
|
298
|
+
# BIP62 rule #6
|
299
|
+
return false if opts[:verify_cleanstack] && !@scripts[in_idx].stack.empty?
|
300
|
+
|
301
|
+
return sig_valid
|
302
|
+
end
|
303
|
+
|
304
|
+
# verify witness input signature +in_idx+ against the corresponding
|
305
|
+
# output in +outpoint_tx+
|
306
|
+
# outpoint. This arg can also be a Script or TxOut
|
307
|
+
#
|
308
|
+
# options are: verify_sigpushonly, verify_minimaldata, verify_cleanstack, verify_dersig, verify_low_s, verify_strictenc
|
309
|
+
def verify_witness_input_signature(in_idx, outpoint_data, prev_out_amount, block_timestamp=Time.now.to_i, opts={})
|
310
|
+
if @enable_bitcoinconsensus
|
311
|
+
return bitcoinconsensus_verify_script(in_idx, outpoint_data, block_timestamp, opts)
|
312
|
+
end
|
313
|
+
|
314
|
+
outpoint_idx = @in[in_idx].prev_out_index
|
315
|
+
script_sig = ''
|
316
|
+
|
317
|
+
script_pubkey = script_pubkey_from_outpoint_data(outpoint_data, outpoint_idx)
|
318
|
+
script_pubkey = Bitcoin::Script.new(script_pubkey)
|
319
|
+
|
320
|
+
if script_pubkey.is_p2sh?
|
321
|
+
redeem_script = Bitcoin::Script.new(@in[in_idx].script_sig).get_pubkey
|
322
|
+
script_pubkey = Bitcoin::Script.new(redeem_script.htb) if Bitcoin.hash160(redeem_script) == script_pubkey.get_hash160 # P2SH-P2WPKH or P2SH-P2WSH
|
323
|
+
end
|
324
|
+
|
325
|
+
@in[in_idx].script_witness.stack.each{|s|script_sig << Bitcoin::Script.pack_pushdata(s)}
|
326
|
+
code_separator_index = 0
|
327
|
+
|
328
|
+
if script_pubkey.is_witness_v0_keyhash? # P2WPKH
|
329
|
+
@scripts[in_idx] = Bitcoin::Script.new(script_sig, Bitcoin::Script.to_hash160_script(script_pubkey.get_hash160))
|
330
|
+
elsif script_pubkey.is_witness_v0_scripthash? # P2WSH
|
331
|
+
witness_hex = @in[in_idx].script_witness.stack.last.bth
|
332
|
+
witness_script = Bitcoin::Script.new(witness_hex.htb)
|
333
|
+
return false unless Bitcoin.sha256(witness_hex) == script_pubkey.get_hash160
|
334
|
+
@scripts[in_idx] = Bitcoin::Script.new(script_sig, Bitcoin::Script.to_p2sh_script(Bitcoin.hash160(witness_hex)))
|
335
|
+
else
|
336
|
+
return false
|
337
|
+
end
|
338
|
+
|
339
|
+
return false if opts[:verify_sigpushonly] && !@scripts[in_idx].is_push_only?(script_sig)
|
340
|
+
return false if opts[:verify_minimaldata] && !@scripts[in_idx].pushes_are_canonical?
|
341
|
+
sig_valid = @scripts[in_idx].run(block_timestamp, opts) do |pubkey,sig,hash_type,subscript|
|
342
|
+
if script_pubkey.is_witness_v0_keyhash?
|
343
|
+
hash = signature_hash_for_witness_input(in_idx, script_pubkey.to_payload, prev_out_amount, nil, hash_type)
|
344
|
+
elsif script_pubkey.is_witness_v0_scripthash?
|
345
|
+
hash = signature_hash_for_witness_input(in_idx, script_pubkey.to_payload, prev_out_amount, witness_hex.htb, hash_type, code_separator_index)
|
346
|
+
code_separator_index += 1 if witness_script.codeseparator_count > code_separator_index
|
347
|
+
end
|
348
|
+
Bitcoin.verify_signature( hash, sig, pubkey.unpack("H*")[0] )
|
349
|
+
end
|
350
|
+
# BIP62 rule #6
|
351
|
+
return false if opts[:verify_cleanstack] && !@scripts[in_idx].stack.empty?
|
352
|
+
|
353
|
+
return sig_valid
|
354
|
+
end
|
355
|
+
|
356
|
+
def bitcoinconsensus_verify_script(in_idx, outpoint_data, block_timestamp=Time.now.to_i, opts={})
|
357
|
+
raise "Bitcoin::BitcoinConsensus shared library not found" unless Bitcoin::BitcoinConsensus.lib_available?
|
358
|
+
|
359
|
+
outpoint_idx = @in[in_idx].prev_out_index
|
360
|
+
script_pubkey = script_pubkey_from_outpoint_data(outpoint_data, outpoint_idx)
|
361
|
+
|
362
|
+
flags = Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_NONE
|
363
|
+
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_P2SH if block_timestamp >= 1333238400
|
364
|
+
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_SIGPUSHONLY if opts[:verify_sigpushonly]
|
365
|
+
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_MINIMALDATA if opts[:verify_minimaldata]
|
366
|
+
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_CLEANSTACK if opts[:verify_cleanstack]
|
367
|
+
flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_LOW_S if opts[:verify_low_s]
|
368
|
+
|
369
|
+
payload ||= to_payload
|
370
|
+
Bitcoin::BitcoinConsensus.verify_script(in_idx, script_pubkey, payload, flags)
|
371
|
+
end
|
372
|
+
|
373
|
+
# convert to ruby hash (see also #from_hash)
|
374
|
+
def to_hash(options = {})
|
375
|
+
@hash ||= hash_from_payload(to_old_payload)
|
376
|
+
h = {
|
377
|
+
'hash' => @hash, 'ver' => @ver, # 'nid' => normalized_hash,
|
378
|
+
'vin_sz' => @in.size, 'vout_sz' => @out.size,
|
379
|
+
'lock_time' => @lock_time, 'size' => (@payload ||= to_payload).bytesize,
|
380
|
+
'in' => @in.map{|i|i.to_hash(options)},
|
381
|
+
'out' => @out.map{|o| o.to_hash(options) }
|
382
|
+
}
|
383
|
+
h['nid'] = normalized_hash if options[:with_nid]
|
384
|
+
h
|
385
|
+
end
|
386
|
+
|
387
|
+
# generates rawblock json as seen in the block explorer.
|
388
|
+
def to_json(options = {:space => ''}, *a)
|
389
|
+
JSON.pretty_generate( to_hash(options), options )
|
390
|
+
end
|
391
|
+
|
392
|
+
# write json representation to a file
|
393
|
+
# (see also #to_json)
|
394
|
+
def to_json_file(path)
|
395
|
+
File.open(path, 'wb'){|f| f.print to_json; }
|
396
|
+
end
|
397
|
+
|
398
|
+
# parse ruby hash (see also #to_hash)
|
399
|
+
def self.from_hash(h, do_raise=true)
|
400
|
+
tx = new(nil)
|
401
|
+
tx.ver, tx.lock_time = (h['ver'] || h['version']), h['lock_time']
|
402
|
+
ins = h['in'] || h['inputs']
|
403
|
+
outs = h['out'] || h['outputs']
|
404
|
+
ins .each{|input|
|
405
|
+
tx.add_in(TxIn.from_hash(input))
|
406
|
+
}
|
407
|
+
outs.each{|output| tx.add_out TxOut.from_hash(output) }
|
408
|
+
tx.instance_eval{
|
409
|
+
@hash = hash_from_payload(to_old_payload)
|
410
|
+
@payload = to_payload
|
411
|
+
}
|
412
|
+
if h['hash'] && (h['hash'] != tx.hash)
|
413
|
+
raise "Tx hash mismatch! Claimed: #{h['hash']}, Actual: #{tx.hash}" if do_raise
|
414
|
+
end
|
415
|
+
tx
|
416
|
+
end
|
417
|
+
|
418
|
+
# convert ruby hash to raw binary
|
419
|
+
def self.binary_from_hash(h)
|
420
|
+
tx = from_hash(h)
|
421
|
+
tx.to_payload
|
422
|
+
end
|
423
|
+
|
424
|
+
# parse json representation
|
425
|
+
def self.from_json(json_string); from_hash( JSON.load(json_string) ); end
|
426
|
+
|
427
|
+
# convert json representation to raw binary
|
428
|
+
def self.binary_from_json(json_string); from_json(json_string).to_payload; end
|
429
|
+
|
430
|
+
# read binary block from a file
|
431
|
+
def self.from_file(path); new( Bitcoin::Protocol.read_binary_file(path) ); end
|
432
|
+
|
433
|
+
# read json block from a file
|
434
|
+
def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
|
435
|
+
|
436
|
+
def size
|
437
|
+
payload.bytesize
|
438
|
+
end
|
439
|
+
|
440
|
+
# Checks if transaction is final taking into account height and time
|
441
|
+
# of a block in which it is located (or about to be included if it's unconfirmed tx).
|
442
|
+
def is_final?(block_height, block_time)
|
443
|
+
# No time lock - tx is final.
|
444
|
+
return true if lock_time == 0
|
445
|
+
|
446
|
+
# Time based nLockTime implemented in 0.1.6
|
447
|
+
# If lock_time is below the magic threshold treat it as a block height.
|
448
|
+
# If lock_time is above the threshold, it's a unix timestamp.
|
449
|
+
return true if lock_time < (lock_time < Bitcoin::LOCKTIME_THRESHOLD ? block_height : block_time)
|
450
|
+
|
451
|
+
inputs.each{|input| return false if !input.is_final? }
|
452
|
+
|
453
|
+
return true
|
454
|
+
end
|
455
|
+
|
456
|
+
def legacy_sigops_count
|
457
|
+
# Note: input scripts normally never have any opcodes since every input script
|
458
|
+
# can be statically reduced to a pushdata-only script.
|
459
|
+
# However, anyone is allowed to create a non-standard transaction with any opcodes in the inputs.
|
460
|
+
count = 0
|
461
|
+
self.in.each do |txin|
|
462
|
+
count += Bitcoin::Script.new(txin.script_sig).sigops_count_accurate(false)
|
463
|
+
end
|
464
|
+
self.out.each do |txout|
|
465
|
+
count += Bitcoin::Script.new(txout.pk_script).sigops_count_accurate(false)
|
466
|
+
end
|
467
|
+
count
|
468
|
+
end
|
469
|
+
|
470
|
+
DEFAULT_BLOCK_PRIORITY_SIZE = 27000
|
471
|
+
|
472
|
+
def minimum_relay_fee; calculate_minimum_fee(allow_free=true, :relay); end
|
473
|
+
def minimum_block_fee; calculate_minimum_fee(allow_free=true, :block); end
|
474
|
+
|
475
|
+
def calculate_minimum_fee(allow_free=true, mode=:block)
|
476
|
+
# Base fee is either nMinTxFee or nMinRelayTxFee
|
477
|
+
base_fee = (mode == :relay) ? Bitcoin.network[:min_relay_tx_fee] : Bitcoin.network[:min_tx_fee]
|
478
|
+
tx_size = to_payload.bytesize
|
479
|
+
min_fee = (1 + tx_size / 1_000) * base_fee
|
480
|
+
|
481
|
+
if allow_free
|
482
|
+
# There is a free transaction area in blocks created by most miners,
|
483
|
+
# * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000
|
484
|
+
# to be considered to fall into this category. We don't want to encourage sending
|
485
|
+
# multiple transactions instead of one big transaction to avoid fees.
|
486
|
+
# * If we are creating a transaction we allow transactions up to 1,000 bytes
|
487
|
+
# to be considered safe and assume they can likely make it into this section.
|
488
|
+
min_fee = 0 if tx_size < (mode == :block ? Bitcoin.network[:free_tx_bytes] : DEFAULT_BLOCK_PRIORITY_SIZE - 1_000)
|
489
|
+
end
|
490
|
+
|
491
|
+
# This code can be removed after enough miners have upgraded to version 0.9.
|
492
|
+
# Until then, be safe when sending and require a fee if any output is less than CENT
|
493
|
+
if min_fee < base_fee && mode == :block
|
494
|
+
outputs.each do |output|
|
495
|
+
if output.value < Bitcoin.network[:dust]
|
496
|
+
# If per dust fee, then we add min fee for each output less than dust.
|
497
|
+
# Otherwise, we set to min fee if there is any output less than dust.
|
498
|
+
if Bitcoin.network[:per_dust_fee]
|
499
|
+
min_fee += base_fee
|
500
|
+
else
|
501
|
+
min_fee = base_fee
|
502
|
+
break
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
min_fee = Bitcoin::network[:max_money] unless min_fee.between?(0, Bitcoin::network[:max_money])
|
509
|
+
min_fee
|
510
|
+
end
|
511
|
+
|
512
|
+
def is_coinbase?
|
513
|
+
inputs.size == 1 and inputs.first.coinbase?
|
514
|
+
end
|
515
|
+
|
516
|
+
def normalized_hash
|
517
|
+
signature_hash_for_input(-1, nil, SIGHASH_TYPE[:all]).reverse.hth
|
518
|
+
end
|
519
|
+
alias :nhash :normalized_hash
|
520
|
+
|
521
|
+
# get witness hash
|
522
|
+
def witness_hash
|
523
|
+
hash_from_payload(to_witness_payload)
|
524
|
+
end
|
525
|
+
|
526
|
+
# sort transaction inputs and outputs under BIP 69
|
527
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki
|
528
|
+
def lexicographical_sort!
|
529
|
+
inputs.sort_by!{|i| [i.previous_output, i.prev_out_index]}
|
530
|
+
outputs.sort_by!{|o| [o.amount, o.pk_script.bth]}
|
531
|
+
end
|
532
|
+
|
533
|
+
private
|
534
|
+
|
535
|
+
def signature_hash_for_input_bip143(input_idx, script_code, prev_out_value, hash_type)
|
536
|
+
hash_prevouts = Digest::SHA256.digest(Digest::SHA256.digest(@in.map{|i| [i.prev_out_hash, i.prev_out_index].pack("a32V")}.join))
|
537
|
+
hash_sequence = Digest::SHA256.digest(Digest::SHA256.digest(@in.map{|i|i.sequence}.join))
|
538
|
+
outpoint = [@in[input_idx].prev_out_hash, @in[input_idx].prev_out_index].pack("a32V")
|
539
|
+
amount = [prev_out_value].pack("Q")
|
540
|
+
nsequence = @in[input_idx].sequence
|
541
|
+
|
542
|
+
hash_outputs = Digest::SHA256.digest(Digest::SHA256.digest(@out.map{|o|o.to_payload}.join))
|
543
|
+
|
544
|
+
case (hash_type & 0x1f)
|
545
|
+
when SIGHASH_TYPE[:single]
|
546
|
+
hash_outputs = input_idx >= @out.size ? "\x00".ljust(32, "\x00") : Digest::SHA256.digest(Digest::SHA256.digest(@out[input_idx].to_payload))
|
547
|
+
hash_sequence = "\x00".ljust(32, "\x00")
|
548
|
+
when SIGHASH_TYPE[:none]
|
549
|
+
hash_sequence = hash_outputs = "\x00".ljust(32, "\x00")
|
550
|
+
end
|
551
|
+
|
552
|
+
if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
|
553
|
+
hash_prevouts = hash_sequence ="\x00".ljust(32, "\x00")
|
554
|
+
end
|
555
|
+
|
556
|
+
buf = [ [@ver].pack("V"), hash_prevouts, hash_sequence, outpoint,
|
557
|
+
script_code, amount, nsequence, hash_outputs, [@lock_time, hash_type].pack("VV")].join
|
558
|
+
|
559
|
+
Digest::SHA256.digest( Digest::SHA256.digest( buf ) )
|
560
|
+
end
|
561
|
+
|
562
|
+
def script_pubkey_from_outpoint_data(outpoint_data, outpoint_idx)
|
563
|
+
if outpoint_data.respond_to?(:out)
|
564
|
+
# If given an entire previous transaction, take the script from it
|
565
|
+
outpoint_data.out[outpoint_idx].pk_script
|
566
|
+
elsif outpoint_data.respond_to?(:pk_script)
|
567
|
+
# If given an transaction output, take the script
|
568
|
+
outpoint_data.pk_script
|
569
|
+
else
|
570
|
+
# Otherwise, we assume it's already a script.
|
571
|
+
outpoint_data
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
def amount_from_outpoint_data(outpoint_data, outpoint_idx)
|
576
|
+
if outpoint_data.respond_to?(:out)
|
577
|
+
# If given an entire previous transaction, take the amount from the
|
578
|
+
# output at the outpoint_idx
|
579
|
+
outpoint_data.out[outpoint_idx].amount
|
580
|
+
elsif outpoint_data.respond_to?(:pk_script)
|
581
|
+
# If given an transaction output, take the amount
|
582
|
+
outpoint_data.amount
|
583
|
+
end
|
584
|
+
end
|
585
|
+
end
|
586
|
+
end
|
587
|
+
end
|