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