sibit 0.28.0 → 0.29.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 +4 -4
- data/Gemfile +2 -0
- data/Gemfile.lock +15 -3
- data/README.md +14 -17
- data/Rakefile +16 -3
- data/bin/sibit +1 -4
- data/features/cli.feature +1 -1
- data/features/dry.feature +25 -0
- data/lib/sibit/bestof.rb +68 -71
- data/lib/sibit/bitcoin/base58.rb +32 -33
- data/lib/sibit/bitcoin/key.rb +64 -65
- data/lib/sibit/bitcoin/script.rb +45 -46
- data/lib/sibit/bitcoin/tx.rb +153 -144
- data/lib/sibit/bitcoin/txbuilder.rb +65 -54
- data/lib/sibit/bitcoinchain.rb +93 -96
- data/lib/sibit/blockchain.rb +115 -118
- data/lib/sibit/blockchair.rb +62 -65
- data/lib/sibit/btc.rb +147 -150
- data/lib/sibit/cex.rb +49 -50
- data/lib/sibit/cryptoapis.rb +113 -116
- data/lib/sibit/fake.rb +50 -47
- data/lib/sibit/firstof.rb +73 -76
- data/lib/sibit/http.rb +10 -6
- data/lib/sibit/json.rb +63 -66
- data/lib/sibit/version.rb +1 -1
- data/lib/sibit.rb +18 -18
- metadata +2 -1
data/lib/sibit/bitcoin/script.rb
CHANGED
|
@@ -6,53 +6,52 @@
|
|
|
6
6
|
require 'digest'
|
|
7
7
|
require_relative 'base58'
|
|
8
8
|
|
|
9
|
+
# Sibit main class.
|
|
9
10
|
class Sibit
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@bytes
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
Base58.encode(versioned + checksum)
|
|
55
|
-
end
|
|
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.new(versioned).check
|
|
54
|
+
Base58.new(versioned + checksum).encode
|
|
56
55
|
end
|
|
57
56
|
end
|
|
58
57
|
end
|
data/lib/sibit/bitcoin/tx.rb
CHANGED
|
@@ -8,160 +8,61 @@ require_relative 'base58'
|
|
|
8
8
|
require_relative 'key'
|
|
9
9
|
require_relative 'script'
|
|
10
10
|
|
|
11
|
+
# Sibit main class.
|
|
11
12
|
class Sibit
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def add_input(hash:, index:, script:, key:)
|
|
31
|
-
@inputs << Input.new(hash, index, script, key)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def add_output(value, address)
|
|
35
|
-
@outputs << Output.new(value, address)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def hash
|
|
39
|
-
Digest::SHA256.hexdigest(Digest::SHA256.digest(payload)).reverse.scan(/../).join
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def payload
|
|
43
|
-
sign_inputs
|
|
44
|
-
serialize
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def hex
|
|
48
|
-
payload.unpack1('H*')
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def in
|
|
52
|
-
@inputs
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def out
|
|
56
|
-
@outputs
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
private
|
|
60
|
-
|
|
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
|
|
68
|
-
end
|
|
69
|
-
|
|
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
|
|
75
|
-
|
|
76
|
-
def sign(key, hash)
|
|
77
|
-
der = key.sign(hash)
|
|
78
|
-
repack(der)
|
|
79
|
-
end
|
|
13
|
+
MIN_TX_FEE = 10_000
|
|
14
|
+
|
|
15
|
+
# Bitcoin Transaction structure.
|
|
16
|
+
#
|
|
17
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
18
|
+
# Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
|
|
19
|
+
# License:: MIT
|
|
20
|
+
class Tx
|
|
21
|
+
SIGHASH_ALL = 0x01
|
|
22
|
+
VERSION = 1
|
|
23
|
+
SEQUENCE = 0xffffffff
|
|
24
|
+
|
|
25
|
+
attr_reader :inputs, :outputs
|
|
26
|
+
|
|
27
|
+
def initialize
|
|
28
|
+
@inputs = []
|
|
29
|
+
@outputs = []
|
|
30
|
+
end
|
|
80
31
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
32
|
+
def add_input(hash:, index:, script:, key:)
|
|
33
|
+
@inputs << Input.new(hash, index, script, key)
|
|
34
|
+
end
|
|
92
35
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
|
|
97
|
-
s <= order / 2
|
|
98
|
-
end
|
|
36
|
+
def add_output(value, address)
|
|
37
|
+
@outputs << Output.new(value, address)
|
|
38
|
+
end
|
|
99
39
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
end
|
|
40
|
+
def hash
|
|
41
|
+
Digest::SHA256.hexdigest(Digest::SHA256.digest(payload)).reverse.scan(/../).join
|
|
42
|
+
end
|
|
104
43
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
44
|
+
def payload
|
|
45
|
+
sign_inputs
|
|
46
|
+
serialize
|
|
47
|
+
end
|
|
108
48
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
49
|
+
def hex
|
|
50
|
+
payload.unpack1('H*')
|
|
51
|
+
end
|
|
129
52
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
|
149
|
-
result += varint(script.length)
|
|
150
|
-
result += script
|
|
151
|
-
end
|
|
152
|
-
result += [0].pack('V')
|
|
153
|
-
result
|
|
154
|
-
end
|
|
53
|
+
def in
|
|
54
|
+
@inputs
|
|
55
|
+
end
|
|
155
56
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return [0xfd, num].pack('Cv') if num <= 0xffff
|
|
159
|
-
return [0xfe, num].pack('CV') if num <= 0xffffffff
|
|
160
|
-
[0xff, num].pack('CQ<')
|
|
161
|
-
end
|
|
57
|
+
def out
|
|
58
|
+
@outputs
|
|
162
59
|
end
|
|
163
60
|
|
|
164
61
|
# Transaction input.
|
|
62
|
+
#
|
|
63
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
64
|
+
# Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
|
|
65
|
+
# License:: MIT
|
|
165
66
|
class Input
|
|
166
67
|
attr_reader :hash, :index, :prev_script, :key
|
|
167
68
|
attr_accessor :script_sig
|
|
@@ -184,6 +85,10 @@ class Sibit
|
|
|
184
85
|
end
|
|
185
86
|
|
|
186
87
|
# Transaction output.
|
|
88
|
+
#
|
|
89
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
90
|
+
# Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
|
|
91
|
+
# License:: MIT
|
|
187
92
|
class Output
|
|
188
93
|
attr_reader :value
|
|
189
94
|
|
|
@@ -204,9 +109,113 @@ class Sibit
|
|
|
204
109
|
private
|
|
205
110
|
|
|
206
111
|
def address_to_hash160(addr)
|
|
207
|
-
decoded = Base58.
|
|
112
|
+
decoded = Base58.new(addr).decode
|
|
208
113
|
decoded[2..41]
|
|
209
114
|
end
|
|
210
115
|
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def sign_inputs
|
|
120
|
+
@inputs.each_with_index do |input, idx|
|
|
121
|
+
sighash = signature_hash(idx)
|
|
122
|
+
sig = sign(input.key, sighash)
|
|
123
|
+
pubkey = [input.key.pub].pack('H*')
|
|
124
|
+
input.script_sig = der_sig(sig) + pubkey_script(pubkey)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def signature_hash(idx)
|
|
129
|
+
tx_copy = serialize_for_signing(idx)
|
|
130
|
+
hash_type = [SIGHASH_ALL].pack('V')
|
|
131
|
+
Digest::SHA256.digest(Digest::SHA256.digest(tx_copy + hash_type))
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def sign(key, hash)
|
|
135
|
+
der = key.sign(hash)
|
|
136
|
+
repack(der)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def repack(der)
|
|
140
|
+
return der if low_s?(der)
|
|
141
|
+
seq = OpenSSL::ASN1.decode(der)
|
|
142
|
+
r = seq.value[0].value.to_i
|
|
143
|
+
s = seq.value[1].value.to_i
|
|
144
|
+
order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
|
|
145
|
+
s = order - s if s > order / 2
|
|
146
|
+
OpenSSL::ASN1::Sequence.new(
|
|
147
|
+
[OpenSSL::ASN1::Integer.new(r), OpenSSL::ASN1::Integer.new(s)]
|
|
148
|
+
).to_der
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def low_s?(der)
|
|
152
|
+
seq = OpenSSL::ASN1.decode(der)
|
|
153
|
+
s = seq.value[1].value.to_i
|
|
154
|
+
order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
|
|
155
|
+
s <= order / 2
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def der_sig(sig)
|
|
159
|
+
data = sig + [SIGHASH_ALL].pack('C')
|
|
160
|
+
[data.length].pack('C') + data
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def pubkey_script(pubkey)
|
|
164
|
+
[pubkey.length].pack('C') + pubkey
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def serialize
|
|
168
|
+
result = [VERSION].pack('V')
|
|
169
|
+
result += varint(@inputs.length)
|
|
170
|
+
@inputs.each do |input|
|
|
171
|
+
result += [input.hash].pack('H*').reverse
|
|
172
|
+
result += [input.index].pack('V')
|
|
173
|
+
result += varint(input.script_sig.length)
|
|
174
|
+
result += input.script_sig
|
|
175
|
+
result += [SEQUENCE].pack('V')
|
|
176
|
+
end
|
|
177
|
+
result += varint(@outputs.length)
|
|
178
|
+
@outputs.each do |output|
|
|
179
|
+
result += [output.value].pack('Q<')
|
|
180
|
+
script = output.script
|
|
181
|
+
result += varint(script.length)
|
|
182
|
+
result += script
|
|
183
|
+
end
|
|
184
|
+
result += [0].pack('V')
|
|
185
|
+
result
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def serialize_for_signing(idx)
|
|
189
|
+
result = [VERSION].pack('V')
|
|
190
|
+
result += varint(@inputs.length)
|
|
191
|
+
@inputs.each_with_index do |input, i|
|
|
192
|
+
result += [input.hash].pack('H*').reverse
|
|
193
|
+
result += [input.index].pack('V')
|
|
194
|
+
if i == idx
|
|
195
|
+
script = [input.prev_script].pack('H*')
|
|
196
|
+
result += varint(script.length)
|
|
197
|
+
result += script
|
|
198
|
+
else
|
|
199
|
+
result += varint(0)
|
|
200
|
+
end
|
|
201
|
+
result += [SEQUENCE].pack('V')
|
|
202
|
+
end
|
|
203
|
+
result += varint(@outputs.length)
|
|
204
|
+
@outputs.each do |output|
|
|
205
|
+
result += [output.value].pack('Q<')
|
|
206
|
+
script = output.script
|
|
207
|
+
result += varint(script.length)
|
|
208
|
+
result += script
|
|
209
|
+
end
|
|
210
|
+
result += [0].pack('V')
|
|
211
|
+
result
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def varint(num)
|
|
215
|
+
return [num].pack('C') if num < 0xfd
|
|
216
|
+
return [0xfd, num].pack('Cv') if num <= 0xffff
|
|
217
|
+
return [0xfe, num].pack('CV') if num <= 0xffffffff
|
|
218
|
+
[0xff, num].pack('CQ<')
|
|
219
|
+
end
|
|
211
220
|
end
|
|
212
221
|
end
|
|
@@ -3,57 +3,60 @@
|
|
|
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
|
+
# Sibit main class.
|
|
9
10
|
class Sibit
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
end
|
|
11
|
+
# Bitcoin Transaction Builder.
|
|
12
|
+
#
|
|
13
|
+
# Provides a similar interface to Bitcoin::Builder::TxBuilder for
|
|
14
|
+
# building and signing Bitcoin transactions.
|
|
15
|
+
#
|
|
16
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
17
|
+
# Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
|
|
18
|
+
# License:: MIT
|
|
19
|
+
class TxBuilder
|
|
20
|
+
def initialize
|
|
21
|
+
@inputs = []
|
|
22
|
+
@outputs = []
|
|
23
|
+
end
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
def input
|
|
26
|
+
inp = Input.new
|
|
27
|
+
yield inp
|
|
28
|
+
@inputs << inp
|
|
29
|
+
end
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
def output(value, address)
|
|
32
|
+
@outputs << { value: value, address: address }
|
|
33
|
+
end
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
end
|
|
35
|
+
def tx(input_value:, leave_fee:, extra_fee:, change_address:)
|
|
36
|
+
txn = Tx.new
|
|
37
|
+
@inputs.each do |inp|
|
|
38
|
+
txn.add_input(
|
|
39
|
+
hash: inp.prev_out_hash,
|
|
40
|
+
index: inp.prev_out_idx,
|
|
41
|
+
script: inp.script,
|
|
42
|
+
key: inp.key
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
total_out = @outputs.sum { |o| o[:value] }
|
|
46
|
+
@outputs.each { |o| txn.add_output(o[:value], o[:address]) }
|
|
47
|
+
if leave_fee
|
|
48
|
+
change = input_value - total_out - extra_fee
|
|
49
|
+
txn.add_output(change, change_address) if change.positive?
|
|
50
|
+
end
|
|
51
|
+
Built.new(txn, @inputs, @outputs)
|
|
53
52
|
end
|
|
54
53
|
|
|
55
54
|
# Input builder for collecting input parameters.
|
|
56
|
-
|
|
55
|
+
#
|
|
56
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
57
|
+
# Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
|
|
58
|
+
# License:: MIT
|
|
59
|
+
class Input
|
|
57
60
|
attr_reader :prev_out_hash, :prev_out_idx, :script, :key
|
|
58
61
|
|
|
59
62
|
def prev_out(hash)
|
|
@@ -74,11 +77,15 @@ class Sibit
|
|
|
74
77
|
end
|
|
75
78
|
|
|
76
79
|
# Wrapper for built transaction with convenience methods.
|
|
77
|
-
|
|
80
|
+
#
|
|
81
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
82
|
+
# Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
|
|
83
|
+
# License:: MIT
|
|
84
|
+
class Built
|
|
78
85
|
def initialize(txn, inputs, outputs)
|
|
79
86
|
@tx = txn
|
|
80
|
-
@
|
|
81
|
-
@
|
|
87
|
+
@inputs = inputs
|
|
88
|
+
@outputs = outputs
|
|
82
89
|
end
|
|
83
90
|
|
|
84
91
|
def hash
|
|
@@ -102,18 +109,22 @@ class Sibit
|
|
|
102
109
|
end
|
|
103
110
|
|
|
104
111
|
def to_payload
|
|
105
|
-
|
|
112
|
+
Payload.new(@tx.payload)
|
|
106
113
|
end
|
|
107
|
-
end
|
|
108
114
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
# Wrapper for payload with hex conversion.
|
|
116
|
+
#
|
|
117
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
118
|
+
# Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
|
|
119
|
+
# License:: MIT
|
|
120
|
+
class Payload
|
|
121
|
+
def initialize(bytes)
|
|
122
|
+
@bytes = bytes
|
|
123
|
+
end
|
|
114
124
|
|
|
115
|
-
|
|
116
|
-
|
|
125
|
+
def bth
|
|
126
|
+
@bytes.unpack1('H*')
|
|
127
|
+
end
|
|
117
128
|
end
|
|
118
129
|
end
|
|
119
130
|
end
|