bitcoinrb 0.0.1
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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bitcoinrb.gemspec +32 -0
- data/exe/bitcoinrb-cli +5 -0
- data/exe/bitcoinrbd +49 -0
- data/lib/bitcoin.rb +121 -0
- data/lib/bitcoin/base58.rb +40 -0
- data/lib/bitcoin/block_header.rb +41 -0
- data/lib/bitcoin/chain_params.rb +57 -0
- data/lib/bitcoin/chainparams/mainnet.yml +25 -0
- data/lib/bitcoin/chainparams/regtest.yml +20 -0
- data/lib/bitcoin/chainparams/testnet.yml +24 -0
- data/lib/bitcoin/connection.rb +66 -0
- data/lib/bitcoin/ext_key.rb +205 -0
- data/lib/bitcoin/key.rb +131 -0
- data/lib/bitcoin/logger.rb +18 -0
- data/lib/bitcoin/merkle_tree.rb +120 -0
- data/lib/bitcoin/message.rb +42 -0
- data/lib/bitcoin/message/addr.rb +74 -0
- data/lib/bitcoin/message/base.rb +40 -0
- data/lib/bitcoin/message/block.rb +41 -0
- data/lib/bitcoin/message/error.rb +10 -0
- data/lib/bitcoin/message/fee_filter.rb +27 -0
- data/lib/bitcoin/message/filter_add.rb +28 -0
- data/lib/bitcoin/message/filter_clear.rb +17 -0
- data/lib/bitcoin/message/filter_load.rb +43 -0
- data/lib/bitcoin/message/get_addr.rb +17 -0
- data/lib/bitcoin/message/get_blocks.rb +29 -0
- data/lib/bitcoin/message/get_data.rb +21 -0
- data/lib/bitcoin/message/get_headers.rb +28 -0
- data/lib/bitcoin/message/handler.rb +170 -0
- data/lib/bitcoin/message/headers.rb +34 -0
- data/lib/bitcoin/message/headers_parser.rb +24 -0
- data/lib/bitcoin/message/inv.rb +21 -0
- data/lib/bitcoin/message/inventories_parser.rb +23 -0
- data/lib/bitcoin/message/inventory.rb +47 -0
- data/lib/bitcoin/message/mem_pool.rb +17 -0
- data/lib/bitcoin/message/merkle_block.rb +42 -0
- data/lib/bitcoin/message/not_found.rb +29 -0
- data/lib/bitcoin/message/ping.rb +30 -0
- data/lib/bitcoin/message/pong.rb +26 -0
- data/lib/bitcoin/message/reject.rb +46 -0
- data/lib/bitcoin/message/send_cmpct.rb +43 -0
- data/lib/bitcoin/message/send_headers.rb +16 -0
- data/lib/bitcoin/message/tx.rb +30 -0
- data/lib/bitcoin/message/ver_ack.rb +17 -0
- data/lib/bitcoin/message/version.rb +79 -0
- data/lib/bitcoin/mnemonic.rb +76 -0
- data/lib/bitcoin/mnemonic/wordlist/chinese_simplified.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/chinese_traditional.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/english.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/french.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/italian.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/japanese.txt +2048 -0
- data/lib/bitcoin/mnemonic/wordlist/spanish.txt +2048 -0
- data/lib/bitcoin/nodes.rb +5 -0
- data/lib/bitcoin/nodes/spv.rb +13 -0
- data/lib/bitcoin/nodes/spv/cli.rb +12 -0
- data/lib/bitcoin/nodes/spv/daemon.rb +21 -0
- data/lib/bitcoin/opcodes.rb +172 -0
- data/lib/bitcoin/out_point.rb +31 -0
- data/lib/bitcoin/script/script.rb +347 -0
- data/lib/bitcoin/script/script_error.rb +168 -0
- data/lib/bitcoin/script/script_interpreter.rb +694 -0
- data/lib/bitcoin/script/tx_checker.rb +44 -0
- data/lib/bitcoin/script_witness.rb +29 -0
- data/lib/bitcoin/secp256k1.rb +10 -0
- data/lib/bitcoin/secp256k1/native.rb +22 -0
- data/lib/bitcoin/secp256k1/ruby.rb +96 -0
- data/lib/bitcoin/tx.rb +191 -0
- data/lib/bitcoin/tx_in.rb +45 -0
- data/lib/bitcoin/tx_out.rb +32 -0
- data/lib/bitcoin/util.rb +105 -0
- data/lib/bitcoin/version.rb +3 -0
- metadata +256 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
class TxChecker
|
3
|
+
|
4
|
+
attr_reader :tx
|
5
|
+
attr_reader :input_index
|
6
|
+
attr_reader :amount
|
7
|
+
|
8
|
+
def initialize(tx: nil, amount: 0, input_index: nil)
|
9
|
+
@tx = tx
|
10
|
+
@amount = amount
|
11
|
+
@input_index = input_index
|
12
|
+
end
|
13
|
+
|
14
|
+
# check signature
|
15
|
+
# @param [String] script_sig
|
16
|
+
# @param [String] pubkey
|
17
|
+
# @param [Bitcoin::Script] script_code
|
18
|
+
# @param [Integer] sig_version
|
19
|
+
def check_sig(script_sig, pubkey, script_code, sig_version)
|
20
|
+
return false if script_sig.empty?
|
21
|
+
script_sig = script_sig.htb
|
22
|
+
hash_type = script_sig[-1].unpack('C').first
|
23
|
+
sig = script_sig[0..-2]
|
24
|
+
if sig_version == ScriptInterpreter::SIG_VERSION[:witness_v0]
|
25
|
+
sighash = tx.sighash_for_input(input_index: input_index, hash_type: hash_type,
|
26
|
+
script_code: script_code, amount: amount, sig_version: sig_version)
|
27
|
+
else
|
28
|
+
sighash = tx.sighash_for_input(input_index: input_index, hash_type: hash_type,
|
29
|
+
script_code: script_code, sig_version: sig_version)
|
30
|
+
end
|
31
|
+
key = Bitcoin::Key.new(pubkey: pubkey)
|
32
|
+
key.verify(sig, sighash)
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_locktime
|
36
|
+
# TODO
|
37
|
+
end
|
38
|
+
|
39
|
+
def check_sequence
|
40
|
+
# TODO
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
# witness
|
4
|
+
class ScriptWitness
|
5
|
+
|
6
|
+
attr_reader :stack
|
7
|
+
|
8
|
+
def initialize(stack = [])
|
9
|
+
@stack = stack
|
10
|
+
end
|
11
|
+
|
12
|
+
def empty?
|
13
|
+
stack.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_payload
|
17
|
+
p = Bitcoin.pack_var_int(stack.size)
|
18
|
+
p << stack.map { |s|
|
19
|
+
Bitcoin.pack_var_int(s.bytesize) << s
|
20
|
+
}.join
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
stack.map{|s|s.bth}.join(' ')
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Secp256k1
|
3
|
+
|
4
|
+
# secp256k1 module using libsecp256k1
|
5
|
+
module Native
|
6
|
+
|
7
|
+
def generate_key_pair(compressed: true)
|
8
|
+
# TODO
|
9
|
+
end
|
10
|
+
|
11
|
+
def generate_key(compressed: true)
|
12
|
+
# TODO
|
13
|
+
end
|
14
|
+
|
15
|
+
def sign_data(privkey, data)
|
16
|
+
# TODO
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module Secp256k1
|
3
|
+
|
4
|
+
GROUP = ECDSA::Group::Secp256k1
|
5
|
+
|
6
|
+
# secp256 module using ecdsa gem
|
7
|
+
# https://github.com/DavidEGrayson/ruby_ecdsa
|
8
|
+
module Ruby
|
9
|
+
|
10
|
+
module_function
|
11
|
+
|
12
|
+
# generate ecdsa private key and public key
|
13
|
+
def generate_key_pair(compressed: true)
|
14
|
+
private_key = 1 + SecureRandom.random_number(GROUP.order - 1)
|
15
|
+
public_key = GROUP.generator.multiply_by_scalar(private_key)
|
16
|
+
privkey = ECDSA::Format::IntegerOctetString.encode(private_key, 32)
|
17
|
+
pubkey = ECDSA::Format::PointOctetString.encode(public_key, compression: compressed)
|
18
|
+
[privkey.bth, pubkey.bth]
|
19
|
+
end
|
20
|
+
|
21
|
+
# generate bitcoin key
|
22
|
+
def generate_key(compressed: true)
|
23
|
+
privkey, pubkey = generate_key_pair(compressed: compressed)
|
24
|
+
Bitcoin::Key.new(priv_key: privkey, pubkey: pubkey, compressed: compressed)
|
25
|
+
end
|
26
|
+
|
27
|
+
# generate publick key from private key
|
28
|
+
# @param [String] privkey a private key with string format
|
29
|
+
# @param [Boolean] compressed pubkey compressed?
|
30
|
+
# @return [String] a pubkey which generate from privkey
|
31
|
+
def generate_pubkey(privkey, compressed: true)
|
32
|
+
private_key = ECDSA::Format::IntegerOctetString.decode(privkey.htb)
|
33
|
+
public_key = GROUP.generator.multiply_by_scalar(private_key)
|
34
|
+
pubkey = ECDSA::Format::PointOctetString.encode(public_key, compression: compressed)
|
35
|
+
pubkey.bth
|
36
|
+
end
|
37
|
+
|
38
|
+
# sign data.
|
39
|
+
# @param [String] data a data to be signed
|
40
|
+
# @param [String] privkey a private key using sign
|
41
|
+
# @return [String] signature data with binary format
|
42
|
+
def sign_data(data, privkey)
|
43
|
+
digest = Digest::SHA2.digest(data)
|
44
|
+
private_key = ECDSA::Format::IntegerOctetString.decode(privkey.htb)
|
45
|
+
signature = nil
|
46
|
+
while signature.nil?
|
47
|
+
# TODO support rfc 6979 https://tools.ietf.org/html/rfc6979
|
48
|
+
temp_key = 1 + SecureRandom.random_number(GROUP.order - 1)
|
49
|
+
signature = ECDSA.sign(GROUP, private_key, digest, temp_key)
|
50
|
+
end
|
51
|
+
ECDSA::Format::SignatureDerString.encode(signature) # signature with DER format
|
52
|
+
end
|
53
|
+
|
54
|
+
# verify signature using public key
|
55
|
+
# @param [String] digest a SHA-256 message digest with binary format
|
56
|
+
# @param [String] sig a signature for +data+ with binary format
|
57
|
+
# @param [String] pubkey a public key corresponding to the private key used for sign
|
58
|
+
# @return [Boolean] verify result
|
59
|
+
def verify_sig(digest, sig, pubkey)
|
60
|
+
begin
|
61
|
+
k = ECDSA::Format::PointOctetString.decode(repack_pubkey(pubkey), GROUP)
|
62
|
+
signature = repack_sig(sig)
|
63
|
+
ECDSA.valid_signature?(k, digest, signature)
|
64
|
+
rescue Exception
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# repack signature for OpenSSL 1.0.1k handling of DER signatures
|
70
|
+
# https://github.com/bitcoin/bitcoin/pull/5634/files
|
71
|
+
def repack_sig(sig)
|
72
|
+
sig_array = sig.unpack('C*')
|
73
|
+
len_r = sig_array[3]
|
74
|
+
r = sig_array[4...(len_r+4)].pack('C*').bth
|
75
|
+
len_s = sig_array[len_r + 5]
|
76
|
+
s = sig_array[(len_r + 6)...(len_r + 6 + len_s)].pack('C*').bth
|
77
|
+
ECDSA::Signature.new(r.to_i(16), s.to_i(16))
|
78
|
+
end
|
79
|
+
|
80
|
+
# if +pubkey+ is hybrid public key format, it convert uncompressed format.
|
81
|
+
# https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2012-June/001578.html
|
82
|
+
def repack_pubkey(pubkey)
|
83
|
+
p = pubkey.htb
|
84
|
+
case p[0]
|
85
|
+
when "\x06", "\x07"
|
86
|
+
p[0] = "\x04"
|
87
|
+
p
|
88
|
+
else
|
89
|
+
pubkey.htb
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
data/lib/bitcoin/tx.rb
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
# Transaction class
|
4
|
+
class Tx
|
5
|
+
|
6
|
+
MARKER = 0x00
|
7
|
+
FLAG = 0x01
|
8
|
+
|
9
|
+
attr_accessor :version
|
10
|
+
attr_accessor :marker
|
11
|
+
attr_accessor :flag
|
12
|
+
attr_reader :inputs
|
13
|
+
attr_reader :outputs
|
14
|
+
attr_accessor :lock_time
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@inputs = []
|
18
|
+
@outputs = []
|
19
|
+
@version = 1
|
20
|
+
@lock_time = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.parse_from_payload(payload)
|
24
|
+
buf = payload.is_a?(String) ? StringIO.new(payload) : payload
|
25
|
+
tx = new
|
26
|
+
tx.version = buf.read(4).unpack('V').first
|
27
|
+
|
28
|
+
in_count = Bitcoin.unpack_var_int_from_io(buf)
|
29
|
+
witness = false
|
30
|
+
if in_count.zero?
|
31
|
+
tx.marker = 0
|
32
|
+
tx.flag = buf.read(1).unpack('c').first
|
33
|
+
in_count = Bitcoin.unpack_var_int_from_io(buf)
|
34
|
+
witness = true
|
35
|
+
end
|
36
|
+
|
37
|
+
in_count.times do
|
38
|
+
tx.inputs << TxIn.parse_from_payload(buf)
|
39
|
+
end
|
40
|
+
|
41
|
+
out_count = Bitcoin.unpack_var_int_from_io(buf)
|
42
|
+
out_count.times do
|
43
|
+
tx.outputs << TxOut.parse_from_payload(buf)
|
44
|
+
end
|
45
|
+
|
46
|
+
if witness
|
47
|
+
in_count.times do |i|
|
48
|
+
witness_count = Bitcoin.unpack_var_int_from_io(buf)
|
49
|
+
witness_count.times do
|
50
|
+
size = Bitcoin.unpack_var_int_from_io(buf)
|
51
|
+
tx.inputs[i].script_witness.stack << buf.read(size)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
tx.lock_time = buf.read(4).unpack('V').first
|
57
|
+
|
58
|
+
tx
|
59
|
+
end
|
60
|
+
|
61
|
+
def txid
|
62
|
+
Bitcoin.double_sha256(serialize_old_format).reverse.bth
|
63
|
+
end
|
64
|
+
|
65
|
+
def wtxid
|
66
|
+
Bitcoin.double_sha256(to_payload).reverse.bth
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_payload
|
70
|
+
witness? ? serialize_witness_format : serialize_old_format
|
71
|
+
end
|
72
|
+
|
73
|
+
def coinbase_tx?
|
74
|
+
inputs.length == 1 && inputs.first.coinbase?
|
75
|
+
end
|
76
|
+
|
77
|
+
def witness?
|
78
|
+
!inputs.find { |i| !i.script_witness.empty? }.nil?
|
79
|
+
end
|
80
|
+
|
81
|
+
# serialize tx with old tx format
|
82
|
+
def serialize_old_format
|
83
|
+
buf = [version].pack('V')
|
84
|
+
buf << Bitcoin.pack_var_int(inputs.length) << inputs.map(&:to_payload).join
|
85
|
+
buf << Bitcoin.pack_var_int(outputs.length) << outputs.map(&:to_payload).join
|
86
|
+
buf << [lock_time].pack('V')
|
87
|
+
buf
|
88
|
+
end
|
89
|
+
|
90
|
+
# serialize tx with segwit tx format
|
91
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki
|
92
|
+
def serialize_witness_format
|
93
|
+
buf = [version, MARKER, FLAG].pack('Vcc')
|
94
|
+
buf << Bitcoin.pack_var_int(inputs.length) << inputs.map(&:to_payload).join
|
95
|
+
buf << Bitcoin.pack_var_int(outputs.length) << outputs.map(&:to_payload).join
|
96
|
+
buf << witness_payload << [lock_time].pack('V')
|
97
|
+
buf
|
98
|
+
end
|
99
|
+
|
100
|
+
def witness_payload
|
101
|
+
inputs.map { |i| i.script_witness.to_payload }.join
|
102
|
+
end
|
103
|
+
|
104
|
+
# get signature hash
|
105
|
+
# @param [Integer] input_index input index.
|
106
|
+
# @param [Integer] hash_type signature hash type
|
107
|
+
# @param [Bitcoin::Script] script_code script code
|
108
|
+
# @param [Integer] amount bitcoin amount locked in input. required for witness input only.
|
109
|
+
def sighash_for_input(input_index: nil, hash_type: Script::SIGHASH_TYPE[:all], script_code: nil,
|
110
|
+
sig_version: ScriptInterpreter::SIG_VERSION[:base], amount: nil)
|
111
|
+
raise ArgumentError, 'input_index must be specified.' unless input_index
|
112
|
+
raise ArgumentError, 'does not exist input corresponding to input_index.' if input_index >= inputs.size
|
113
|
+
raise ArgumentError, 'script_pubkey must be specified.' unless script_code
|
114
|
+
|
115
|
+
if sig_version == ScriptInterpreter::SIG_VERSION[:witness_v0]
|
116
|
+
raise ArgumentError, 'amount must be specified.' unless amount
|
117
|
+
sighash_for_witness(input_index, script_code, hash_type, amount)
|
118
|
+
else
|
119
|
+
sighash_for_legacy(input_index, script_code, hash_type)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
# generate sighash with legacy format
|
126
|
+
def sighash_for_legacy(index, script_code, hash_type)
|
127
|
+
ins = inputs.map.with_index do |i, idx|
|
128
|
+
if idx == index
|
129
|
+
i.to_payload(script_code)
|
130
|
+
else
|
131
|
+
case hash_type & 0x1f
|
132
|
+
when Script::SIGHASH_TYPE[:none], Script::SIGHASH_TYPE[:single]
|
133
|
+
i.to_payload(Bitcoin::Script.new, 0)
|
134
|
+
else
|
135
|
+
i.to_payload(Bitcoin::Script.new)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
outs = outputs.map(&:to_payload)
|
141
|
+
out_size = Bitcoin.pack_var_int(outputs.size)
|
142
|
+
|
143
|
+
case hash_type & 0x1f
|
144
|
+
when Script::SIGHASH_TYPE[:none]
|
145
|
+
outs = ''
|
146
|
+
out_size = Bitcoin.pack_var_int(0)
|
147
|
+
when Script::SIGHASH_TYPE[:single]
|
148
|
+
return "\x01".ljust(32, "\x00") if index >= outputs.size
|
149
|
+
outs = outputs[0...(index + 1)].map.with_index { |o, idx| (idx == index) ? o.to_payload : o.to_empty_payload }.join
|
150
|
+
out_size = Bitcoin.pack_var_int(index + 1)
|
151
|
+
end
|
152
|
+
|
153
|
+
if hash_type & Script::SIGHASH_TYPE[:anyonecanpay] != 0
|
154
|
+
ins = [ins[index]]
|
155
|
+
end
|
156
|
+
|
157
|
+
buf = [[version].pack('V'), Bitcoin.pack_var_int(ins.size),
|
158
|
+
ins, out_size, outs, [lock_time, hash_type].pack('VV')].join
|
159
|
+
|
160
|
+
Bitcoin.double_sha256(buf)
|
161
|
+
end
|
162
|
+
|
163
|
+
# generate sighash with BIP-143 format
|
164
|
+
# https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
|
165
|
+
def sighash_for_witness(index, script_code, hash_type, amount)
|
166
|
+
hash_prevouts = Bitcoin.double_sha256(inputs.map{|i|i.out_point.to_payload}.join)
|
167
|
+
hash_sequence = Bitcoin.double_sha256(inputs.map{|i|[i.sequence].pack('V')}.join)
|
168
|
+
outpoint = inputs[index].out_point.to_payload
|
169
|
+
amount = [amount].pack('Q')
|
170
|
+
nsequence = [inputs[index].sequence].pack('V')
|
171
|
+
hash_outputs = Bitcoin.double_sha256(outputs.map{|o|o.to_payload}.join)
|
172
|
+
|
173
|
+
case (hash_type & 0x1f)
|
174
|
+
when Script::SIGHASH_TYPE[:single]
|
175
|
+
hash_outputs = index >= outputs.size ? "\x00".ljust(32, "\x00") : Bitcoin.double_sha256(outputs[index].to_payload)
|
176
|
+
hash_sequence = "\x00".ljust(32, "\x00")
|
177
|
+
when Script::SIGHASH_TYPE[:none]
|
178
|
+
hash_sequence = hash_outputs = "\x00".ljust(32, "\x00")
|
179
|
+
end
|
180
|
+
|
181
|
+
if (hash_type & Script::SIGHASH_TYPE[:anyonecanpay]) != 0
|
182
|
+
hash_prevouts = hash_sequence ="\x00".ljust(32, "\x00")
|
183
|
+
end
|
184
|
+
buf = [ [version].pack('V'), hash_prevouts, hash_sequence, outpoint, Bitcoin::Script.pack_pushdata(script_code.to_payload),
|
185
|
+
amount, nsequence, hash_outputs, [@lock_time, hash_type].pack('VV')].join
|
186
|
+
Bitcoin.double_sha256(buf)
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
# transaction input
|
4
|
+
class TxIn
|
5
|
+
|
6
|
+
attr_accessor :out_point
|
7
|
+
attr_accessor :script_sig
|
8
|
+
attr_accessor :sequence
|
9
|
+
attr_accessor :script_witness
|
10
|
+
|
11
|
+
DEFAULT_SEQUENCE = 0xFFFFFFFF
|
12
|
+
|
13
|
+
def initialize(out_point: nil, script_sig: nil, script_witness: ScriptWitness.new, sequence: DEFAULT_SEQUENCE)
|
14
|
+
@out_point = out_point
|
15
|
+
@script_sig = script_sig
|
16
|
+
@script_witness = script_witness
|
17
|
+
@sequence = sequence
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse_from_payload(payload)
|
21
|
+
buf = payload.is_a?(String) ? StringIO.new(payload) : payload
|
22
|
+
i = new
|
23
|
+
hash, index = buf.read(36).unpack('a32V')
|
24
|
+
i.out_point = OutPoint.new(hash.reverse.bth, index)
|
25
|
+
sig_length = Bitcoin.unpack_var_int_from_io(buf)
|
26
|
+
sig = buf.read(sig_length)
|
27
|
+
i.script_sig = Script.new
|
28
|
+
i.script_sig.chunks[0] = sig
|
29
|
+
i.sequence = buf.read(4).unpack('V').first
|
30
|
+
i
|
31
|
+
end
|
32
|
+
|
33
|
+
def coinbase?
|
34
|
+
out_point.coinbase?
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_payload(script_sig = @script_sig, sequence = @sequence)
|
38
|
+
p = out_point.to_payload
|
39
|
+
p << Bitcoin.pack_var_int(script_sig.to_payload.bytesize)
|
40
|
+
p << script_sig.to_payload << [sequence].pack('V')
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
|
3
|
+
# transaction output
|
4
|
+
class TxOut
|
5
|
+
|
6
|
+
attr_accessor :value
|
7
|
+
attr_accessor :script_pubkey
|
8
|
+
|
9
|
+
def initialize(value: 0, script_pubkey: nil)
|
10
|
+
@value = value
|
11
|
+
@script_pubkey = script_pubkey
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.parse_from_payload(payload)
|
15
|
+
buf = payload.is_a?(String) ? StringIO.new(payload) : payload
|
16
|
+
value = buf.read(8).unpack('Q').first
|
17
|
+
script_size = Bitcoin.unpack_var_int_from_io(buf)
|
18
|
+
new(value: value, script_pubkey: Script.parse_from_payload(buf.read(script_size)))
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_payload
|
22
|
+
s = script_pubkey.to_payload
|
23
|
+
[value].pack('Q') << Bitcoin.pack_var_int(s.length) << s
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_empty_payload
|
27
|
+
'ffffffffffffffff00'.htb
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|