tapyrus 0.3.8 → 0.4.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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +2 -2
- data/.ruby-version +1 -1
- data/README.md +43 -2
- data/lib/tapyrus/ext_key.rb +8 -4
- data/lib/tapyrus/key.rb +1 -1
- data/lib/tapyrus/key_path.rb +17 -13
- data/lib/tapyrus/message/fee_filter.rb +2 -2
- data/lib/tapyrus/message/header_and_short_ids.rb +4 -4
- data/lib/tapyrus/message/inventory.rb +1 -1
- data/lib/tapyrus/message/network_addr.rb +2 -2
- data/lib/tapyrus/message/ping.rb +2 -2
- data/lib/tapyrus/message/pong.rb +2 -2
- data/lib/tapyrus/message/send_cmpct.rb +2 -2
- data/lib/tapyrus/message/version.rb +3 -3
- data/lib/tapyrus/mnemonic.rb +9 -4
- data/lib/tapyrus/pstt/input.rb +422 -0
- data/lib/tapyrus/pstt/key_origin_info.rb +63 -0
- data/lib/tapyrus/pstt/output.rb +129 -0
- data/lib/tapyrus/pstt/proprietary.rb +54 -0
- data/lib/tapyrus/pstt/tx.rb +658 -0
- data/lib/tapyrus/pstt.rb +335 -0
- data/lib/tapyrus/rpc.rb +0 -2
- data/lib/tapyrus/script/script.rb +14 -4
- data/lib/tapyrus/secp256k1/rfc6979.rb +6 -3
- data/lib/tapyrus/tip0020.rb +341 -0
- data/lib/tapyrus/tx.rb +6 -2
- data/lib/tapyrus/tx_out.rb +2 -2
- data/lib/tapyrus/util.rb +3 -3
- data/lib/tapyrus/version.rb +1 -1
- data/lib/tapyrus/wallet/account.rb +2 -2
- data/lib/tapyrus/wallet/db.rb +3 -3
- data/lib/tapyrus/wallet/master_key.rb +2 -2
- data/lib/tapyrus.rb +3 -3
- data/tapyrusrb.gemspec +0 -4
- metadata +10 -81
- data/exe/tapyrusrb-cli +0 -5
- data/exe/tapyrusrbd +0 -42
- data/lib/tapyrus/network/connection.rb +0 -70
- data/lib/tapyrus/network/message_handler.rb +0 -244
- data/lib/tapyrus/network/peer.rb +0 -210
- data/lib/tapyrus/network/peer_discovery.rb +0 -43
- data/lib/tapyrus/network/pool.rb +0 -135
- data/lib/tapyrus/network.rb +0 -11
- data/lib/tapyrus/node/cli.rb +0 -114
- data/lib/tapyrus/node/configuration.rb +0 -36
- data/lib/tapyrus/node/spv.rb +0 -78
- data/lib/tapyrus/node.rb +0 -7
- data/lib/tapyrus/rpc/http_server.rb +0 -64
- data/lib/tapyrus/rpc/request_handler.rb +0 -146
data/lib/tapyrus/pstt.rb
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
module Tapyrus
|
|
2
|
+
# Partially Signed Tapyrus Transaction (PSTT) defined by TIP-0174.
|
|
3
|
+
#
|
|
4
|
+
# A PSTT holds a not-yet-signed Tapyrus transaction together with the metadata signers need.
|
|
5
|
+
# The data model follows BIP-370: the transaction is represented by per-input and per-output
|
|
6
|
+
# fields, so inputs and outputs can be added after creation.
|
|
7
|
+
module PSTT
|
|
8
|
+
autoload :Input, "tapyrus/pstt/input"
|
|
9
|
+
autoload :KeyOriginInfo, "tapyrus/pstt/key_origin_info"
|
|
10
|
+
autoload :Output, "tapyrus/pstt/output"
|
|
11
|
+
autoload :Proprietary, "tapyrus/pstt/proprietary"
|
|
12
|
+
autoload :Tx, "tapyrus/pstt/tx"
|
|
13
|
+
|
|
14
|
+
# The magic bytes which begin every PSTT. ASCII "pstt" followed by the separator 0xFF.
|
|
15
|
+
MAGIC = "70737474ff".htb
|
|
16
|
+
|
|
17
|
+
# The only version this TIP defines.
|
|
18
|
+
VERSION = 0
|
|
19
|
+
|
|
20
|
+
# The boundary between height-based and time-based locktime.
|
|
21
|
+
LOCKTIME_THRESHOLD = 500_000_000
|
|
22
|
+
|
|
23
|
+
# The sighash types TIP-0174 defines: SIGHASH_ALL, SIGHASH_NONE and SIGHASH_SINGLE, each
|
|
24
|
+
# optionally combined with SIGHASH_ANYONECANPAY.
|
|
25
|
+
SIGHASH_BASE_TYPES = [SIGHASH_TYPE[:all], SIGHASH_TYPE[:none], SIGHASH_TYPE[:single]].freeze
|
|
26
|
+
SIGHASH_TYPES = (SIGHASH_BASE_TYPES + SIGHASH_BASE_TYPES.map { |t| t | SIGHASH_TYPE[:anyonecanpay] }).freeze
|
|
27
|
+
|
|
28
|
+
# Global map key types.
|
|
29
|
+
module GlobalTypes
|
|
30
|
+
# Reserved. The global unsigned transaction of BIP-174 has no counterpart in this format.
|
|
31
|
+
UNSIGNED_TX = 0x00
|
|
32
|
+
XPUB = 0x01
|
|
33
|
+
TX_FEATURES = 0x02
|
|
34
|
+
FALLBACK_LOCKTIME = 0x03
|
|
35
|
+
INPUT_COUNT = 0x04
|
|
36
|
+
OUTPUT_COUNT = 0x05
|
|
37
|
+
TX_MODIFIABLE = 0x06
|
|
38
|
+
VERSION = 0xfb
|
|
39
|
+
PROPRIETARY = 0xfc
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Input map key types.
|
|
43
|
+
module InputTypes
|
|
44
|
+
UTXO = 0x00
|
|
45
|
+
PARTIAL_SIG = 0x02
|
|
46
|
+
SIGHASH_TYPE = 0x03
|
|
47
|
+
REDEEM_SCRIPT = 0x04
|
|
48
|
+
BIP32_DERIVATION = 0x06
|
|
49
|
+
FINAL_SCRIPTSIG = 0x07
|
|
50
|
+
RIPEMD160 = 0x0a
|
|
51
|
+
SHA256 = 0x0b
|
|
52
|
+
HASH160 = 0x0c
|
|
53
|
+
HASH256 = 0x0d
|
|
54
|
+
PREVIOUS_TXID = 0x0e
|
|
55
|
+
OUTPUT_INDEX = 0x0f
|
|
56
|
+
SEQUENCE = 0x10
|
|
57
|
+
REQUIRED_TIME_LOCKTIME = 0x11
|
|
58
|
+
REQUIRED_HEIGHT_LOCKTIME = 0x12
|
|
59
|
+
PROPRIETARY = 0xfc
|
|
60
|
+
|
|
61
|
+
# Segregated Witness(0x01, 0x05, 0x08, 0x09) and Taproot(0x13..0x18, defined by BIP-371)
|
|
62
|
+
# key types. They have no meaning in Tapyrus. Key types which BIPs later assigned to
|
|
63
|
+
# MuSig2 and other schemes are not listed here, so that they are carried through as
|
|
64
|
+
# unknown records rather than rejected.
|
|
65
|
+
RESERVED = [0x01, 0x05, 0x08, 0x09, *(0x13..0x18)].freeze
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Output map key types.
|
|
69
|
+
module OutputTypes
|
|
70
|
+
REDEEM_SCRIPT = 0x00
|
|
71
|
+
BIP32_DERIVATION = 0x02
|
|
72
|
+
AMOUNT = 0x03
|
|
73
|
+
SCRIPT = 0x04
|
|
74
|
+
PROPRIETARY = 0xfc
|
|
75
|
+
|
|
76
|
+
# Segregated Witness(0x01) and Taproot(0x05..0x07, defined by BIP-371) key types.
|
|
77
|
+
# They have no meaning in Tapyrus. See InputTypes::RESERVED.
|
|
78
|
+
RESERVED = [0x01, *(0x05..0x07)].freeze
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# The bitfield of PSTT_GLOBAL_TX_MODIFIABLE.
|
|
82
|
+
module TxModifiable
|
|
83
|
+
INPUTS = 0x01
|
|
84
|
+
OUTPUTS = 0x02
|
|
85
|
+
HAS_SIGHASH_SINGLE = 0x04
|
|
86
|
+
ALL = INPUTS | OUTPUTS | HAS_SIGHASH_SINGLE
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Raised when a PSTT is malformed, or when an operation violates a rule of TIP-0174.
|
|
90
|
+
class Error < StandardError
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# A single key-value record of a PSTT map.
|
|
94
|
+
KeyValue =
|
|
95
|
+
Struct.new(:type, :keydata, :value) do
|
|
96
|
+
# The complete key(<keytype> and <keydata>). Records are unique by this value within a map.
|
|
97
|
+
# @return [String] the complete key with binary format.
|
|
98
|
+
def key
|
|
99
|
+
Tapyrus.pack_var_int(type) + keydata
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def to_payload
|
|
103
|
+
k = key
|
|
104
|
+
Tapyrus.pack_var_int(k.bytesize) + k + Tapyrus.pack_var_int(value.bytesize) + value
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class << self
|
|
109
|
+
# Read a compact size unsigned integer from +buf+.
|
|
110
|
+
#
|
|
111
|
+
# Every compact size integer of the format must be minimally encoded. TIP-0174 states the
|
|
112
|
+
# requirement for <keytype>, and holding the whole container to it is what makes the byte
|
|
113
|
+
# representation of a PSTT unique: two encodings of the same length would otherwise produce
|
|
114
|
+
# two different byte strings carrying the same records. It also removes the need to treat
|
|
115
|
+
# the map separator specially, since a non-minimally encoded 0 is rejected here.
|
|
116
|
+
# @param [StringIO] buf a buffer.
|
|
117
|
+
# @return [Integer] the value.
|
|
118
|
+
# @raise [Tapyrus::PSTT::Error] if the buffer is truncated or the encoding is not minimal.
|
|
119
|
+
def read_compact_size(buf)
|
|
120
|
+
prefix = read_bytes(buf, 1).unpack1("C")
|
|
121
|
+
case prefix
|
|
122
|
+
when 0xfd
|
|
123
|
+
value = read_bytes(buf, 2).unpack1("v")
|
|
124
|
+
raise Error, "compact size is not minimally encoded." if value < 0xfd
|
|
125
|
+
when 0xfe
|
|
126
|
+
value = read_bytes(buf, 4).unpack1("V")
|
|
127
|
+
raise Error, "compact size is not minimally encoded." if value <= 0xffff
|
|
128
|
+
when 0xff
|
|
129
|
+
value = read_bytes(buf, 8).unpack1("Q<")
|
|
130
|
+
raise Error, "compact size is not minimally encoded." if value <= 0xffffffff
|
|
131
|
+
else
|
|
132
|
+
value = prefix
|
|
133
|
+
end
|
|
134
|
+
value
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Read exactly +size+ bytes from +buf+.
|
|
138
|
+
# @param [StringIO] buf a buffer.
|
|
139
|
+
# @param [Integer] size the byte size to be read.
|
|
140
|
+
# @return [String] the read data with binary format.
|
|
141
|
+
# @raise [Tapyrus::PSTT::Error] if the buffer holds fewer bytes.
|
|
142
|
+
def read_bytes(buf, size)
|
|
143
|
+
data = buf.read(size)
|
|
144
|
+
raise Error, "PSTT payload is truncated." if data.nil? || data.bytesize != size
|
|
145
|
+
data
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Parse a single map, which is a sequence of key-value records terminated by 0x00.
|
|
149
|
+
# @param [StringIO] buf a buffer.
|
|
150
|
+
# @return [Array[Tapyrus::PSTT::KeyValue]] the records of the map.
|
|
151
|
+
# @raise [Tapyrus::PSTT::Error] if the map is malformed.
|
|
152
|
+
def parse_map(buf)
|
|
153
|
+
records = []
|
|
154
|
+
keys = {}
|
|
155
|
+
loop do
|
|
156
|
+
raise Error, "PSTT map is not terminated." if buf.eof?
|
|
157
|
+
key_len = read_compact_size(buf)
|
|
158
|
+
break if key_len.zero? # the 0x00 separator
|
|
159
|
+
key = read_bytes(buf, key_len)
|
|
160
|
+
key_buf = StringIO.new(key)
|
|
161
|
+
type = read_compact_size(key_buf)
|
|
162
|
+
keydata = key_buf.read || ""
|
|
163
|
+
raise Error, "PSTT map contains a duplicated key." if keys.key?(key)
|
|
164
|
+
keys[key] = true
|
|
165
|
+
value_len = read_compact_size(buf)
|
|
166
|
+
records << KeyValue.new(type, keydata, read_bytes(buf, value_len))
|
|
167
|
+
end
|
|
168
|
+
records
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Serialize +records+ as a map.
|
|
172
|
+
# @param [Array[Tapyrus::PSTT::KeyValue]] records the records of the map.
|
|
173
|
+
# @param [Array[String]] order the complete keys of the map this PSTT was parsed from.
|
|
174
|
+
# @return [String] the serialized map with binary format.
|
|
175
|
+
def serialize_map(records, order = nil)
|
|
176
|
+
order_records(records, order).map(&:to_payload).join + "\x00".b
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Put +records+ back into the order the map was parsed in, so that parsing a PSTT and
|
|
180
|
+
# serializing it again yields the same bytes. TIP-0174 prescribes no order - either order is
|
|
181
|
+
# a valid PSTT carrying the same records - but a byte-stable round trip lets a caller hash,
|
|
182
|
+
# cache or diff a PSTT without normalizing it first. A record which was not in the parsed
|
|
183
|
+
# map, such as a signature collected since, follows the records which were, in the order
|
|
184
|
+
# this implementation generates them. A map which was not parsed is emitted in ascending
|
|
185
|
+
# order of the complete key.
|
|
186
|
+
# @param [Array[Tapyrus::PSTT::KeyValue]] records the records of the map.
|
|
187
|
+
# @param [Array[String]] order the complete keys of the map this PSTT was parsed from.
|
|
188
|
+
# @return [Array[Tapyrus::PSTT::KeyValue]] the ordered records.
|
|
189
|
+
def order_records(records, order)
|
|
190
|
+
return records.sort_by { |r| [r.type, r.keydata] } if order.nil? || order.empty?
|
|
191
|
+
rank = {}
|
|
192
|
+
order.each_with_index { |key, i| rank[key] ||= i }
|
|
193
|
+
records.each_with_index.sort_by { |record, i| [rank.fetch(record.key, order.size + i), i] }.map(&:first)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Check that +record+ has no key data, as its field definition requires.
|
|
197
|
+
# @param [Tapyrus::PSTT::KeyValue] record a record.
|
|
198
|
+
# @raise [Tapyrus::PSTT::Error] if the record has key data.
|
|
199
|
+
def validate_empty_keydata!(record)
|
|
200
|
+
raise Error, format("keydata for type 0x%02x must be empty.", record.type) unless record.keydata.empty?
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Check that the key data of +record+ is a public key.
|
|
204
|
+
# @param [Tapyrus::PSTT::KeyValue] record a record.
|
|
205
|
+
# @raise [Tapyrus::PSTT::Error] if the key data is not a 33- or 65-byte public key.
|
|
206
|
+
def validate_pubkey_keydata!(record)
|
|
207
|
+
size = record.keydata.bytesize
|
|
208
|
+
unless [Tapyrus::Key::COMPRESSED_PUBLIC_KEY_SIZE, Tapyrus::Key::PUBLIC_KEY_SIZE].include?(size)
|
|
209
|
+
raise Error,
|
|
210
|
+
format("keydata for type 0x%02x must be a 33- or 65-byte public key, but %d bytes.", record.type, size)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Check that the value of +record+ has +size+ bytes.
|
|
215
|
+
# @param [Tapyrus::PSTT::KeyValue] record a record.
|
|
216
|
+
# @param [Integer] size the required byte size.
|
|
217
|
+
# @raise [Tapyrus::PSTT::Error] if the value has another size.
|
|
218
|
+
def validate_value_size!(record, size)
|
|
219
|
+
unless record.value.bytesize == size
|
|
220
|
+
raise Error,
|
|
221
|
+
format(
|
|
222
|
+
"value for type 0x%02x must be %d bytes, but %d bytes.",
|
|
223
|
+
record.type,
|
|
224
|
+
size,
|
|
225
|
+
record.value.bytesize
|
|
226
|
+
)
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Read a 32-bit little endian unsigned integer from the value of +record+.
|
|
231
|
+
def read_u32(record)
|
|
232
|
+
validate_value_size!(record, 4)
|
|
233
|
+
record.value.unpack1("V")
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Read a 32-bit little endian signed integer from the value of +record+.
|
|
237
|
+
def read_i32(record)
|
|
238
|
+
validate_value_size!(record, 4)
|
|
239
|
+
record.value.unpack1("l<")
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Read a 64-bit little endian signed integer from the value of +record+.
|
|
243
|
+
def read_i64(record)
|
|
244
|
+
validate_value_size!(record, 8)
|
|
245
|
+
record.value.unpack1("q<")
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Read an 8-bit unsigned integer from the value of +record+.
|
|
249
|
+
def read_u8(record)
|
|
250
|
+
validate_value_size!(record, 1)
|
|
251
|
+
record.value.unpack1("C")
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# Read a compact size unsigned integer from the value of +record+.
|
|
255
|
+
def read_count(record)
|
|
256
|
+
buf = StringIO.new(record.value)
|
|
257
|
+
count = read_compact_size(buf)
|
|
258
|
+
raise Error, format("value for type 0x%02x is not a compact size uint.", record.type) unless buf.eof?
|
|
259
|
+
count
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Parse the value or the key data of a record with +block+, and report any failure as a
|
|
263
|
+
# Tapyrus::PSTT::Error. The records of a PSTT come from another party, so a malformed one
|
|
264
|
+
# must not surface as an exception of the parser the field happens to be built on.
|
|
265
|
+
# @param [String] name the name of the field, used in the error message.
|
|
266
|
+
# @raise [Tapyrus::PSTT::Error] if the block raises.
|
|
267
|
+
def parse_field(name)
|
|
268
|
+
yield
|
|
269
|
+
rescue Error
|
|
270
|
+
raise
|
|
271
|
+
rescue StandardError => e
|
|
272
|
+
raise Error, "#{name} is malformed. #{e.message}"
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Check that +hash_type+ is one of the sighash types TIP-0174 defines.
|
|
276
|
+
# @param [Integer] hash_type a sighash type.
|
|
277
|
+
# @raise [Tapyrus::PSTT::Error] if it is not.
|
|
278
|
+
def validate_sighash_type!(hash_type)
|
|
279
|
+
unless SIGHASH_TYPES.include?(hash_type)
|
|
280
|
+
raise Error, format("Sighash type 0x%x is not defined by TIP-0174.", hash_type)
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Check that +amount+ is within the range Tapyrus allows.
|
|
285
|
+
# @param [Integer] amount an amount of tapy or of tokens.
|
|
286
|
+
# @raise [Tapyrus::PSTT::Error] if it is not.
|
|
287
|
+
def validate_amount!(amount)
|
|
288
|
+
unless amount.is_a?(Integer) && amount >= 0 && amount <= MAX_MONEY
|
|
289
|
+
raise Error, "PSTT_OUT_AMOUNT must be between 0 and #{MAX_MONEY}, but #{amount}."
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
# Check that +features+ is a positive value. Tapyrus currently defines only 1, but a
|
|
294
|
+
# transaction feature which a later version adds is accepted so that this implementation
|
|
295
|
+
# can carry it through.
|
|
296
|
+
# @param [Integer] features the value of PSTT_GLOBAL_TX_FEATURES.
|
|
297
|
+
# @raise [Tapyrus::PSTT::Error] if it is not positive.
|
|
298
|
+
def validate_features!(features)
|
|
299
|
+
raise Error, "PSTT_GLOBAL_TX_FEATURES must be greater than 0, but #{features}." unless features.positive?
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
# Check that +flags+ sets no reserved bit of PSTT_GLOBAL_TX_MODIFIABLE.
|
|
303
|
+
# @param [Integer] flags the bitfield.
|
|
304
|
+
# @raise [Tapyrus::PSTT::Error] if a reserved bit is set.
|
|
305
|
+
def validate_tx_modifiable!(flags)
|
|
306
|
+
unless (flags & ~TxModifiable::ALL).zero?
|
|
307
|
+
raise Error, "The reserved bits of PSTT_GLOBAL_TX_MODIFIABLE must be 0."
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Check that +version+ is a version this implementation supports.
|
|
312
|
+
# @param [Integer] version the value of PSTT_GLOBAL_VERSION.
|
|
313
|
+
# @raise [Tapyrus::PSTT::Error] if it is greater than the version TIP-0174 defines.
|
|
314
|
+
def validate_version!(version)
|
|
315
|
+
raise Error, "PSTT version #{version} is not supported." if version > VERSION
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Check that +value+ is a valid PSTT_IN_REQUIRED_TIME_LOCKTIME.
|
|
319
|
+
# @raise [Tapyrus::PSTT::Error] if it is below the threshold.
|
|
320
|
+
def validate_time_locktime!(value)
|
|
321
|
+
if value < LOCKTIME_THRESHOLD
|
|
322
|
+
raise Error, "PSTT_IN_REQUIRED_TIME_LOCKTIME must be greater than or equal to #{LOCKTIME_THRESHOLD}."
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Check that +value+ is a valid PSTT_IN_REQUIRED_HEIGHT_LOCKTIME.
|
|
327
|
+
# @raise [Tapyrus::PSTT::Error] if it is out of range.
|
|
328
|
+
def validate_height_locktime!(value)
|
|
329
|
+
if value.zero? || value >= LOCKTIME_THRESHOLD
|
|
330
|
+
raise Error, "PSTT_IN_REQUIRED_HEIGHT_LOCKTIME must be greater than 0 and less than #{LOCKTIME_THRESHOLD}."
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
end
|
data/lib/tapyrus/rpc.rb
CHANGED
|
@@ -107,12 +107,22 @@ module Tapyrus
|
|
|
107
107
|
if opcode
|
|
108
108
|
script << (v =~ /^\d/ && Opcodes.small_int_to_opcode(v.ord) ? v.ord : opcode)
|
|
109
109
|
else
|
|
110
|
-
script << (v
|
|
110
|
+
script << (number_token?(v) ? v.to_i : v)
|
|
111
111
|
end
|
|
112
112
|
end
|
|
113
113
|
script
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
+
# Whether +token+ is a script number rather than pushed data with hex format.
|
|
117
|
+
# A digit only token is ambiguous since #to_s emits pushed data as hex. #to_s never emits
|
|
118
|
+
# a number with more than 10 digits, so a longer even-length token must be pushed data.
|
|
119
|
+
# @param [String] token a token of the script string.
|
|
120
|
+
# @return [Boolean] whether +token+ should be interpreted as a script number.
|
|
121
|
+
def self.number_token?(token)
|
|
122
|
+
return false unless token =~ /^[0-9]+$/
|
|
123
|
+
token.length < 12 || token.length.odd?
|
|
124
|
+
end
|
|
125
|
+
|
|
116
126
|
# generate script from addr.
|
|
117
127
|
# @param [String] addr address.
|
|
118
128
|
# @return [Tapyrus::Script] parsed script.
|
|
@@ -439,11 +449,11 @@ module Tapyrus
|
|
|
439
449
|
header =
|
|
440
450
|
if size < OP_PUSHDATA1
|
|
441
451
|
[size].pack("C")
|
|
442
|
-
elsif size
|
|
452
|
+
elsif size <= 0xff
|
|
443
453
|
[OP_PUSHDATA1, size].pack("CC")
|
|
444
|
-
elsif size
|
|
454
|
+
elsif size <= 0xffff
|
|
445
455
|
[OP_PUSHDATA2, size].pack("Cv")
|
|
446
|
-
elsif size
|
|
456
|
+
elsif size <= 0xffffffff
|
|
447
457
|
[OP_PUSHDATA4, size].pack("CV")
|
|
448
458
|
else
|
|
449
459
|
raise ArgumentError, "data size is too big."
|
|
@@ -30,10 +30,13 @@ module Tapyrus
|
|
|
30
30
|
v = Tapyrus.hmac_sha256(k, v)
|
|
31
31
|
|
|
32
32
|
# 3.2.h
|
|
33
|
-
t = ""
|
|
34
33
|
10_000.times do
|
|
35
|
-
|
|
36
|
-
t =
|
|
34
|
+
# T is reset for each candidate, otherwise a retry can never produce a value below the order.
|
|
35
|
+
t = ""
|
|
36
|
+
while t.bytesize < 32
|
|
37
|
+
v = Tapyrus.hmac_sha256(k, v)
|
|
38
|
+
t = (t + v)
|
|
39
|
+
end
|
|
37
40
|
t_num = t.bth.to_i(16)
|
|
38
41
|
return t_num if 1 <= t_num && t_num < Tapyrus::Secp256k1::GROUP.order
|
|
39
42
|
k = Tapyrus.hmac_sha256(k, v + "00".htb)
|