sibit 0.27.1 → 0.29.0

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.
@@ -6,53 +6,51 @@
6
6
  require 'digest'
7
7
  require_relative 'base58'
8
8
 
9
- class Sibit
10
- module Bitcoin
11
- # Bitcoin Script parser.
12
- #
13
- # Parses standard P2PKH scripts to extract addresses.
14
- #
15
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
16
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
17
- # License:: MIT
18
- class Script
19
- OP_DUP = 0x76
20
- OP_HASH160 = 0xa9
21
- OP_EQUALVERIFY = 0x88
22
- OP_CHECKSIG = 0xac
23
-
24
- def initialize(hex)
25
- @bytes = [hex].pack('H*').bytes
26
- end
27
-
28
- def address
29
- return p2pkh_address if p2pkh?
30
- nil
31
- end
32
-
33
- def p2pkh?
34
- @bytes.length == 25 &&
35
- @bytes[0] == OP_DUP &&
36
- @bytes[1] == OP_HASH160 &&
37
- @bytes[2] == 20 &&
38
- @bytes[23] == OP_EQUALVERIFY &&
39
- @bytes[24] == OP_CHECKSIG
40
- end
41
-
42
- def hash160
43
- return nil unless p2pkh?
44
- @bytes[3, 20].pack('C*').unpack1('H*')
45
- end
46
-
47
- private
48
-
49
- def p2pkh_address
50
- h = hash160
51
- return nil unless h
52
- versioned = "00#{h}"
53
- checksum = Base58.check(versioned)
54
- Base58.encode(versioned + checksum)
55
- end
9
+ module Sibit::Bitcoin
10
+ # Bitcoin Script parser.
11
+ #
12
+ # Parses standard P2PKH scripts to extract addresses.
13
+ #
14
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
15
+ # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
16
+ # License:: MIT
17
+ class Script
18
+ OP_DUP = 0x76
19
+ OP_HASH160 = 0xa9
20
+ OP_EQUALVERIFY = 0x88
21
+ OP_CHECKSIG = 0xac
22
+
23
+ def initialize(hex)
24
+ @bytes = [hex].pack('H*').bytes
25
+ end
26
+
27
+ def address
28
+ return p2pkh_address if p2pkh?
29
+ nil
30
+ end
31
+
32
+ def p2pkh?
33
+ @bytes.length == 25 &&
34
+ @bytes[0] == OP_DUP &&
35
+ @bytes[1] == OP_HASH160 &&
36
+ @bytes[2] == 20 &&
37
+ @bytes[23] == OP_EQUALVERIFY &&
38
+ @bytes[24] == OP_CHECKSIG
39
+ end
40
+
41
+ def hash160
42
+ return nil unless p2pkh?
43
+ @bytes[3, 20].pack('C*').unpack1('H*')
44
+ end
45
+
46
+ private
47
+
48
+ def p2pkh_address
49
+ h = hash160
50
+ return nil unless h
51
+ versioned = "00#{h}"
52
+ checksum = Base58.check(versioned)
53
+ Base58.encode(versioned + checksum)
56
54
  end
57
55
  end
58
56
  end
@@ -8,205 +8,203 @@ require_relative 'base58'
8
8
  require_relative 'key'
9
9
  require_relative 'script'
10
10
 
11
- class Sibit
12
- module Bitcoin
13
- # Bitcoin Transaction structure.
14
- #
15
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
16
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
17
- # License:: MIT
18
- class Tx
19
- SIGHASH_ALL = 0x01
20
- VERSION = 1
21
- SEQUENCE = 0xffffffff
22
-
23
- attr_reader :inputs, :outputs
24
-
25
- def initialize
26
- @inputs = []
27
- @outputs = []
28
- end
11
+ module Sibit::Bitcoin
12
+ # Bitcoin Transaction structure.
13
+ #
14
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
15
+ # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
16
+ # License:: MIT
17
+ class Tx
18
+ SIGHASH_ALL = 0x01
19
+ VERSION = 1
20
+ SEQUENCE = 0xffffffff
21
+
22
+ attr_reader :inputs, :outputs
23
+
24
+ def initialize
25
+ @inputs = []
26
+ @outputs = []
27
+ end
29
28
 
30
- def add_input(hash:, index:, script:, key:)
31
- @inputs << Input.new(hash, index, script, key)
32
- end
29
+ def add_input(hash:, index:, script:, key:)
30
+ @inputs << Input.new(hash, index, script, key)
31
+ end
33
32
 
34
- def add_output(value, address)
35
- @outputs << Output.new(value, address)
36
- end
33
+ def add_output(value, address)
34
+ @outputs << Output.new(value, address)
35
+ end
37
36
 
38
- def hash
39
- Digest::SHA256.hexdigest(Digest::SHA256.digest(payload)).reverse.scan(/../).join
40
- end
37
+ def hash
38
+ Digest::SHA256.hexdigest(Digest::SHA256.digest(payload)).reverse.scan(/../).join
39
+ end
41
40
 
42
- def payload
43
- sign_inputs
44
- serialize
45
- end
41
+ def payload
42
+ sign_inputs
43
+ serialize
44
+ end
46
45
 
47
- def hex
48
- payload.unpack1('H*')
49
- end
46
+ def hex
47
+ payload.unpack1('H*')
48
+ end
50
49
 
51
- def in
52
- @inputs
53
- end
50
+ def in
51
+ @inputs
52
+ end
54
53
 
55
- def out
56
- @outputs
57
- end
54
+ def out
55
+ @outputs
56
+ end
58
57
 
59
- private
58
+ private
60
59
 
61
- def sign_inputs
62
- @inputs.each_with_index do |input, idx|
63
- sighash = signature_hash(idx)
64
- sig = sign(input.key, sighash)
65
- pubkey = [input.key.pub].pack('H*')
66
- input.script_sig = der_sig(sig) + pubkey_script(pubkey)
67
- end
60
+ def sign_inputs
61
+ @inputs.each_with_index do |input, idx|
62
+ sighash = signature_hash(idx)
63
+ sig = sign(input.key, sighash)
64
+ pubkey = [input.key.pub].pack('H*')
65
+ input.script_sig = der_sig(sig) + pubkey_script(pubkey)
68
66
  end
67
+ end
69
68
 
70
- def signature_hash(idx)
71
- tx_copy = serialize_for_signing(idx)
72
- hash_type = [SIGHASH_ALL].pack('V')
73
- Digest::SHA256.digest(Digest::SHA256.digest(tx_copy + hash_type))
74
- end
69
+ def signature_hash(idx)
70
+ tx_copy = serialize_for_signing(idx)
71
+ hash_type = [SIGHASH_ALL].pack('V')
72
+ Digest::SHA256.digest(Digest::SHA256.digest(tx_copy + hash_type))
73
+ end
75
74
 
76
- def sign(key, hash)
77
- der = key.sign(hash)
78
- repack(der)
79
- end
75
+ def sign(key, hash)
76
+ der = key.sign(hash)
77
+ repack(der)
78
+ end
80
79
 
81
- def repack(der)
82
- return der if low_s?(der)
83
- seq = OpenSSL::ASN1.decode(der)
84
- r = seq.value[0].value.to_i
85
- s = seq.value[1].value.to_i
86
- order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
87
- s = order - s if s > order / 2
88
- OpenSSL::ASN1::Sequence.new(
89
- [OpenSSL::ASN1::Integer.new(r), OpenSSL::ASN1::Integer.new(s)]
90
- ).to_der
91
- end
80
+ def repack(der)
81
+ return der if low_s?(der)
82
+ seq = OpenSSL::ASN1.decode(der)
83
+ r = seq.value[0].value.to_i
84
+ s = seq.value[1].value.to_i
85
+ order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
86
+ s = order - s if s > order / 2
87
+ OpenSSL::ASN1::Sequence.new(
88
+ [OpenSSL::ASN1::Integer.new(r), OpenSSL::ASN1::Integer.new(s)]
89
+ ).to_der
90
+ end
92
91
 
93
- def low_s?(der)
94
- seq = OpenSSL::ASN1.decode(der)
95
- s = seq.value[1].value.to_i
96
- order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
97
- s <= order / 2
98
- end
92
+ def low_s?(der)
93
+ seq = OpenSSL::ASN1.decode(der)
94
+ s = seq.value[1].value.to_i
95
+ order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
96
+ s <= order / 2
97
+ end
99
98
 
100
- def der_sig(sig)
101
- data = sig + [SIGHASH_ALL].pack('C')
102
- [data.length].pack('C') + data
103
- end
99
+ def der_sig(sig)
100
+ data = sig + [SIGHASH_ALL].pack('C')
101
+ [data.length].pack('C') + data
102
+ end
104
103
 
105
- def pubkey_script(pubkey)
106
- [pubkey.length].pack('C') + pubkey
107
- end
104
+ def pubkey_script(pubkey)
105
+ [pubkey.length].pack('C') + pubkey
106
+ end
108
107
 
109
- def serialize
110
- result = [VERSION].pack('V')
111
- result += varint(@inputs.length)
112
- @inputs.each do |input|
113
- result += [input.hash].pack('H*').reverse
114
- result += [input.index].pack('V')
115
- result += varint(input.script_sig.length)
116
- result += input.script_sig
117
- result += [SEQUENCE].pack('V')
118
- end
119
- result += varint(@outputs.length)
120
- @outputs.each do |output|
121
- result += [output.value].pack('Q<')
122
- script = output.script
123
- result += varint(script.length)
124
- result += script
125
- end
126
- result += [0].pack('V')
127
- result
128
- end
108
+ def serialize
109
+ result = [VERSION].pack('V')
110
+ result += varint(@inputs.length)
111
+ @inputs.each do |input|
112
+ result += [input.hash].pack('H*').reverse
113
+ result += [input.index].pack('V')
114
+ result += varint(input.script_sig.length)
115
+ result += input.script_sig
116
+ result += [SEQUENCE].pack('V')
117
+ end
118
+ result += varint(@outputs.length)
119
+ @outputs.each do |output|
120
+ result += [output.value].pack('Q<')
121
+ script = output.script
122
+ result += varint(script.length)
123
+ result += script
124
+ end
125
+ result += [0].pack('V')
126
+ result
127
+ end
129
128
 
130
- def serialize_for_signing(idx)
131
- result = [VERSION].pack('V')
132
- result += varint(@inputs.length)
133
- @inputs.each_with_index do |input, i|
134
- result += [input.hash].pack('H*').reverse
135
- result += [input.index].pack('V')
136
- if i == idx
137
- script = [input.prev_script].pack('H*')
138
- result += varint(script.length)
139
- result += script
140
- else
141
- result += varint(0)
142
- end
143
- result += [SEQUENCE].pack('V')
144
- end
145
- result += varint(@outputs.length)
146
- @outputs.each do |output|
147
- result += [output.value].pack('Q<')
148
- script = output.script
129
+ def serialize_for_signing(idx)
130
+ result = [VERSION].pack('V')
131
+ result += varint(@inputs.length)
132
+ @inputs.each_with_index do |input, i|
133
+ result += [input.hash].pack('H*').reverse
134
+ result += [input.index].pack('V')
135
+ if i == idx
136
+ script = [input.prev_script].pack('H*')
149
137
  result += varint(script.length)
150
138
  result += script
139
+ else
140
+ result += varint(0)
151
141
  end
152
- result += [0].pack('V')
153
- result
142
+ result += [SEQUENCE].pack('V')
154
143
  end
155
-
156
- def varint(num)
157
- return [num].pack('C') if num < 0xfd
158
- return [0xfd, num].pack('Cv') if num <= 0xffff
159
- return [0xfe, num].pack('CV') if num <= 0xffffffff
160
- [0xff, num].pack('CQ<')
144
+ result += varint(@outputs.length)
145
+ @outputs.each do |output|
146
+ result += [output.value].pack('Q<')
147
+ script = output.script
148
+ result += varint(script.length)
149
+ result += script
161
150
  end
151
+ result += [0].pack('V')
152
+ result
162
153
  end
163
154
 
164
- # Transaction input.
165
- class Input
166
- attr_reader :hash, :index, :prev_script, :key
167
- attr_accessor :script_sig
155
+ def varint(num)
156
+ return [num].pack('C') if num < 0xfd
157
+ return [0xfd, num].pack('Cv') if num <= 0xffff
158
+ return [0xfe, num].pack('CV') if num <= 0xffffffff
159
+ [0xff, num].pack('CQ<')
160
+ end
161
+ end
168
162
 
169
- def initialize(hash, index, script, key)
170
- @hash = hash
171
- @index = index
172
- @prev_script = script
173
- @key = key
174
- @script_sig = ''
175
- end
163
+ # Transaction input.
164
+ class Input
165
+ attr_reader :hash, :index, :prev_script, :key
166
+ attr_accessor :script_sig
167
+
168
+ def initialize(hash, index, script, key)
169
+ @hash = hash
170
+ @index = index
171
+ @prev_script = script
172
+ @key = key
173
+ @script_sig = ''
174
+ end
176
175
 
177
- def prev_out
178
- [@hash].pack('H*')
179
- end
176
+ def prev_out
177
+ [@hash].pack('H*')
178
+ end
180
179
 
181
- def prev_out_index
182
- @index
183
- end
180
+ def prev_out_index
181
+ @index
184
182
  end
183
+ end
185
184
 
186
- # Transaction output.
187
- class Output
188
- attr_reader :value
185
+ # Transaction output.
186
+ class Output
187
+ attr_reader :value
189
188
 
190
- def initialize(value, address)
191
- @value = value
192
- @address = address
193
- end
189
+ def initialize(value, address)
190
+ @value = value
191
+ @address = address
192
+ end
194
193
 
195
- def script
196
- hash160 = address_to_hash160(@address)
197
- [0x76, 0xa9, 0x14].pack('C*') + [hash160].pack('H*') + [0x88, 0xac].pack('C*')
198
- end
194
+ def script
195
+ hash160 = address_to_hash160(@address)
196
+ [0x76, 0xa9, 0x14].pack('C*') + [hash160].pack('H*') + [0x88, 0xac].pack('C*')
197
+ end
199
198
 
200
- def script_hex
201
- script.unpack1('H*')
202
- end
199
+ def script_hex
200
+ script.unpack1('H*')
201
+ end
203
202
 
204
- private
203
+ private
205
204
 
206
- def address_to_hash160(addr)
207
- decoded = Base58.decode(addr)
208
- decoded[2..41]
209
- end
205
+ def address_to_hash160(addr)
206
+ decoded = Base58.decode(addr)
207
+ decoded[2..41]
210
208
  end
211
209
  end
212
210
  end
@@ -3,8 +3,8 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
- require_relative 'tx'
7
6
  require_relative 'key'
7
+ require_relative 'tx'
8
8
 
9
9
  class Sibit
10
10
  module Bitcoin