tapyrus 0.1.0 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -15
- data/exe/tapyrusrbd +2 -2
- data/lib/openassets/util.rb +2 -4
- data/lib/schnorr.rb +83 -0
- data/lib/schnorr/signature.rb +38 -0
- data/lib/tapyrus.rb +11 -18
- data/lib/tapyrus/block.rb +3 -38
- data/lib/tapyrus/block_header.rb +70 -41
- data/lib/tapyrus/chain_params.rb +13 -36
- data/lib/tapyrus/chainparams/{regtest.yml → dev.yml} +10 -19
- data/lib/tapyrus/chainparams/{mainnet.yml → prod.yml} +7 -19
- data/lib/tapyrus/constants.rb +12 -34
- data/lib/tapyrus/errors.rb +17 -0
- data/lib/tapyrus/ext.rb +5 -0
- data/lib/tapyrus/ext/ecdsa.rb +39 -0
- data/lib/tapyrus/ext/json_parser.rb +47 -0
- data/lib/tapyrus/ext_key.rb +42 -23
- data/lib/tapyrus/key.rb +47 -40
- data/lib/tapyrus/message.rb +2 -2
- data/lib/tapyrus/message/base.rb +1 -0
- data/lib/tapyrus/message/block.rb +4 -4
- data/lib/tapyrus/message/cmpct_block.rb +3 -5
- data/lib/tapyrus/message/header_and_short_ids.rb +1 -1
- data/lib/tapyrus/message/headers.rb +1 -1
- data/lib/tapyrus/message/merkle_block.rb +1 -1
- data/lib/tapyrus/message/tx.rb +2 -2
- data/lib/tapyrus/network/peer.rb +1 -15
- data/lib/tapyrus/node/cli.rb +15 -11
- data/lib/tapyrus/node/configuration.rb +1 -1
- data/lib/tapyrus/node/spv.rb +1 -1
- data/lib/tapyrus/opcodes.rb +5 -0
- data/lib/tapyrus/out_point.rb +1 -1
- data/lib/tapyrus/rpc/request_handler.rb +7 -6
- data/lib/tapyrus/rpc/tapyrus_core_client.rb +17 -15
- data/lib/tapyrus/script/color.rb +79 -0
- data/lib/tapyrus/script/multisig.rb +0 -27
- data/lib/tapyrus/script/script.rb +74 -89
- data/lib/tapyrus/script/script_error.rb +8 -14
- data/lib/tapyrus/script/script_interpreter.rb +65 -86
- data/lib/tapyrus/script/tx_checker.rb +21 -5
- data/lib/tapyrus/secp256k1.rb +1 -0
- data/lib/tapyrus/secp256k1/native.rb +93 -20
- data/lib/tapyrus/secp256k1/rfc6979.rb +43 -0
- data/lib/tapyrus/secp256k1/ruby.rb +63 -54
- data/lib/tapyrus/store/chain_entry.rb +3 -2
- data/lib/tapyrus/store/db/level_db.rb +58 -0
- data/lib/tapyrus/store/spv_chain.rb +29 -11
- data/lib/tapyrus/tx.rb +18 -160
- data/lib/tapyrus/tx_in.rb +4 -11
- data/lib/tapyrus/tx_out.rb +2 -1
- data/lib/tapyrus/util.rb +8 -0
- data/lib/tapyrus/validation.rb +1 -6
- data/lib/tapyrus/version.rb +1 -1
- data/lib/tapyrus/wallet/account.rb +1 -0
- data/lib/tapyrus/wallet/master_key.rb +1 -0
- data/tapyrusrb.gemspec +3 -3
- metadata +44 -39
- data/lib/tapyrus/chainparams/testnet.yml +0 -41
- data/lib/tapyrus/descriptor.rb +0 -147
- data/lib/tapyrus/script_witness.rb +0 -38
@@ -3,10 +3,12 @@ module Tapyrus
|
|
3
3
|
module Store
|
4
4
|
|
5
5
|
KEY_PREFIX = {
|
6
|
-
entry: 'e',
|
7
|
-
height: 'h',
|
8
|
-
best: 'B',
|
9
|
-
next: 'n'
|
6
|
+
entry: 'e', # key: block hash, value: Tapyrus::Store::ChainEntry payload
|
7
|
+
height: 'h', # key: block height, value: block hash.
|
8
|
+
best: 'B', # value: best block hash.
|
9
|
+
next: 'n', # key: block hash, value: A hash of the next block of the specified hash
|
10
|
+
agg_pubkey: 'a', # key: index, value: Activated block height | aggregated public key.
|
11
|
+
latest_agg_pubkey: 'g' # value: latest agg pubkey index.
|
10
12
|
}
|
11
13
|
|
12
14
|
class SPVChain
|
@@ -14,10 +16,14 @@ module Tapyrus
|
|
14
16
|
attr_reader :db
|
15
17
|
attr_reader :logger
|
16
18
|
|
17
|
-
|
19
|
+
# initialize spv chain
|
20
|
+
# @param[Tapyrus::Store::DB::LevelDB] db
|
21
|
+
# @param[Tapyrus::Block] genesis genesis block
|
22
|
+
def initialize(db = Tapyrus::Store::DB::LevelDB.new, genesis: nil)
|
23
|
+
raise ArgumentError, 'genesis block should be specified.' unless genesis
|
18
24
|
@db = db # TODO multiple db switch
|
19
25
|
@logger = Tapyrus::Logger.create(:debug)
|
20
|
-
initialize_block
|
26
|
+
initialize_block(genesis)
|
21
27
|
end
|
22
28
|
|
23
29
|
# get latest block in the store.
|
@@ -45,9 +51,9 @@ module Tapyrus
|
|
45
51
|
# @return [Tapyrus::Store::ChainEntry] appended block header entry.
|
46
52
|
def append_header(header)
|
47
53
|
logger.info("append header #{header.block_id}")
|
48
|
-
raise "this header is invalid. #{header.block_hash}" unless header.valid?
|
49
54
|
best_block = latest_block
|
50
55
|
current_height = best_block.height
|
56
|
+
raise "this header is invalid. #{header.block_hash}" unless header.valid?(db.agg_pubkey_with_height(current_height + 1))
|
51
57
|
if best_block.block_hash == header.prev_hash
|
52
58
|
entry = Tapyrus::Store::ChainEntry.new(header, current_height + 1)
|
53
59
|
db.save_entry(entry)
|
@@ -83,14 +89,26 @@ module Tapyrus
|
|
83
89
|
time[time.size / 2]
|
84
90
|
end
|
85
91
|
|
92
|
+
# Add aggregated public key.
|
93
|
+
# @param [Integer] active_height
|
94
|
+
# @param [String] agg_pubkey aggregated public key with hex format.
|
95
|
+
def add_agg_pubkey(active_height, agg_pubkey)
|
96
|
+
db.add_agg_pubkey(active_height, agg_pubkey)
|
97
|
+
end
|
98
|
+
|
99
|
+
# get aggregated public key keys.
|
100
|
+
# @return [Array[Array(height, agg_pubkey)]] the list of public keys.
|
101
|
+
def agg_pubkeys
|
102
|
+
db.agg_pubkeys
|
103
|
+
end
|
104
|
+
|
86
105
|
private
|
87
106
|
|
88
107
|
# if database is empty, put genesis block.
|
89
|
-
|
108
|
+
# @param [Tapyrus::Block] genesis genesis block
|
109
|
+
def initialize_block(genesis)
|
90
110
|
unless latest_block
|
91
|
-
|
92
|
-
genesis = ChainEntry.new(block.header, 0)
|
93
|
-
db.save_entry(genesis)
|
111
|
+
db.save_entry(ChainEntry.new(genesis.header, 0))
|
94
112
|
end
|
95
113
|
end
|
96
114
|
|
data/lib/tapyrus/tx.rb
CHANGED
@@ -5,18 +5,14 @@ module Tapyrus
|
|
5
5
|
|
6
6
|
# Transaction class
|
7
7
|
class Tx
|
8
|
+
include Tapyrus::HexConverter
|
8
9
|
|
9
10
|
MAX_STANDARD_VERSION = 2
|
10
11
|
|
11
12
|
# The maximum weight for transactions we're willing to relay/mine
|
12
13
|
MAX_STANDARD_TX_WEIGHT = 400000
|
13
14
|
|
14
|
-
|
15
|
-
FLAG = 0x01
|
16
|
-
|
17
|
-
attr_accessor :version
|
18
|
-
attr_accessor :marker
|
19
|
-
attr_accessor :flag
|
15
|
+
attr_accessor :features
|
20
16
|
attr_reader :inputs
|
21
17
|
attr_reader :outputs
|
22
18
|
attr_accessor :lock_time
|
@@ -24,30 +20,19 @@ module Tapyrus
|
|
24
20
|
def initialize
|
25
21
|
@inputs = []
|
26
22
|
@outputs = []
|
27
|
-
@
|
23
|
+
@features = 1
|
28
24
|
@lock_time = 0
|
29
25
|
end
|
30
26
|
|
31
27
|
alias_method :in, :inputs
|
32
28
|
alias_method :out, :outputs
|
33
29
|
|
34
|
-
def self.parse_from_payload(payload
|
30
|
+
def self.parse_from_payload(payload)
|
35
31
|
buf = payload.is_a?(String) ? StringIO.new(payload) : payload
|
36
32
|
tx = new
|
37
|
-
tx.
|
33
|
+
tx.features = buf.read(4).unpack('V').first
|
38
34
|
|
39
35
|
in_count = Tapyrus.unpack_var_int_from_io(buf)
|
40
|
-
witness = false
|
41
|
-
if in_count.zero? && !non_witness
|
42
|
-
tx.marker = 0
|
43
|
-
tx.flag = buf.read(1).unpack('c').first
|
44
|
-
if tx.flag.zero?
|
45
|
-
buf.pos -= 1
|
46
|
-
else
|
47
|
-
in_count = Tapyrus.unpack_var_int_from_io(buf)
|
48
|
-
witness = true
|
49
|
-
end
|
50
|
-
end
|
51
36
|
|
52
37
|
in_count.times do
|
53
38
|
tx.inputs << TxIn.parse_from_payload(buf)
|
@@ -58,101 +43,46 @@ module Tapyrus
|
|
58
43
|
tx.outputs << TxOut.parse_from_payload(buf)
|
59
44
|
end
|
60
45
|
|
61
|
-
if witness
|
62
|
-
in_count.times do |i|
|
63
|
-
tx.inputs[i].script_witness = Tapyrus::ScriptWitness.parse_from_payload(buf)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
46
|
tx.lock_time = buf.read(4).unpack('V').first
|
68
47
|
|
69
48
|
tx
|
70
49
|
end
|
71
50
|
|
72
51
|
def hash
|
73
|
-
|
52
|
+
to_hex.to_i(16)
|
74
53
|
end
|
75
54
|
|
76
55
|
def tx_hash
|
77
|
-
Tapyrus.double_sha256(
|
56
|
+
Tapyrus.double_sha256(to_payload).bth
|
78
57
|
end
|
79
58
|
|
80
59
|
def txid
|
81
|
-
buf = [
|
60
|
+
buf = [features].pack('V')
|
82
61
|
buf << Tapyrus.pack_var_int(inputs.length) << inputs.map{|i|i.to_payload(use_malfix: true)}.join
|
83
62
|
buf << Tapyrus.pack_var_int(outputs.length) << outputs.map(&:to_payload).join
|
84
63
|
buf << [lock_time].pack('V')
|
85
64
|
Tapyrus.double_sha256(buf).reverse.bth
|
86
65
|
end
|
87
66
|
|
88
|
-
def witness_hash
|
89
|
-
Tapyrus.double_sha256(to_payload).bth
|
90
|
-
end
|
91
|
-
|
92
|
-
def wtxid
|
93
|
-
witness_hash.rhex
|
94
|
-
end
|
95
|
-
|
96
|
-
# get the witness commitment of coinbase tx.
|
97
|
-
# if this tx does not coinbase or not have commitment, return nil.
|
98
|
-
def witness_commitment
|
99
|
-
return nil unless coinbase_tx?
|
100
|
-
outputs.each do |output|
|
101
|
-
commitment = output.script_pubkey.witness_commitment
|
102
|
-
return commitment if commitment
|
103
|
-
end
|
104
|
-
nil
|
105
|
-
end
|
106
|
-
|
107
67
|
def to_payload
|
108
|
-
|
109
|
-
end
|
110
|
-
|
111
|
-
# convert tx to hex format.
|
112
|
-
# @return [String] tx with hex format.
|
113
|
-
def to_hex
|
114
|
-
to_payload.bth
|
115
|
-
end
|
116
|
-
|
117
|
-
def coinbase_tx?
|
118
|
-
inputs.length == 1 && inputs.first.coinbase?
|
119
|
-
end
|
120
|
-
|
121
|
-
def witness?
|
122
|
-
!inputs.find { |i| !i.script_witness.empty? }.nil?
|
123
|
-
end
|
124
|
-
|
125
|
-
def ==(other)
|
126
|
-
to_payload == other.to_payload
|
127
|
-
end
|
128
|
-
|
129
|
-
# serialize tx with old tx format
|
130
|
-
def serialize_old_format
|
131
|
-
buf = [version].pack('V')
|
68
|
+
buf = [features].pack('V')
|
132
69
|
buf << Tapyrus.pack_var_int(inputs.length) << inputs.map(&:to_payload).join
|
133
70
|
buf << Tapyrus.pack_var_int(outputs.length) << outputs.map(&:to_payload).join
|
134
71
|
buf << [lock_time].pack('V')
|
135
72
|
buf
|
136
73
|
end
|
137
74
|
|
138
|
-
|
139
|
-
|
140
|
-
def serialize_witness_format
|
141
|
-
buf = [version, MARKER, FLAG].pack('Vcc')
|
142
|
-
buf << Tapyrus.pack_var_int(inputs.length) << inputs.map(&:to_payload).join
|
143
|
-
buf << Tapyrus.pack_var_int(outputs.length) << outputs.map(&:to_payload).join
|
144
|
-
buf << witness_payload << [lock_time].pack('V')
|
145
|
-
buf
|
75
|
+
def coinbase_tx?
|
76
|
+
inputs.length == 1 && inputs.first.coinbase?
|
146
77
|
end
|
147
78
|
|
148
|
-
def
|
149
|
-
|
79
|
+
def ==(other)
|
80
|
+
to_payload == other.to_payload
|
150
81
|
end
|
151
82
|
|
152
83
|
# check this tx is standard.
|
153
84
|
def standard?
|
154
|
-
return false if
|
155
|
-
return false if weight > MAX_STANDARD_TX_WEIGHT
|
85
|
+
return false if features > MAX_STANDARD_VERSION
|
156
86
|
inputs.each do |i|
|
157
87
|
# Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed keys (remember the 520 byte limit on redeemScript size).
|
158
88
|
# That works out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627
|
@@ -177,21 +107,6 @@ module Tapyrus
|
|
177
107
|
to_payload.bytesize
|
178
108
|
end
|
179
109
|
|
180
|
-
# The virtual transaction size (differs from size for witness transactions)
|
181
|
-
def vsize
|
182
|
-
(weight.to_f / 4).ceil
|
183
|
-
end
|
184
|
-
|
185
|
-
# calculate tx weight
|
186
|
-
# weight = (legacy tx payload) * 3 + (witness tx payload)
|
187
|
-
def weight
|
188
|
-
if witness?
|
189
|
-
serialize_old_format.bytesize * (WITNESS_SCALE_FACTOR - 1) + serialize_witness_format.bytesize
|
190
|
-
else
|
191
|
-
serialize_old_format.bytesize * WITNESS_SCALE_FACTOR
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
110
|
# get signature hash
|
196
111
|
# @param [Integer] input_index input index.
|
197
112
|
# @param [Integer] hash_type signature hash type
|
@@ -205,13 +120,7 @@ module Tapyrus
|
|
205
120
|
raise ArgumentError, 'does not exist input corresponding to input_index.' if input_index >= inputs.size
|
206
121
|
raise ArgumentError, 'script_pubkey must be specified.' unless output_script
|
207
122
|
raise ArgumentError, 'unsupported sig version specified.' unless SIG_VERSION.include?(sig_version)
|
208
|
-
|
209
|
-
if sig_version == :witness_v0 || Tapyrus.chain_params.fork_chain?
|
210
|
-
raise ArgumentError, 'amount must be specified.' unless amount
|
211
|
-
sighash_for_witness(input_index, output_script, hash_type, amount, skip_separator_index)
|
212
|
-
else
|
213
|
-
sighash_for_legacy(input_index, output_script, hash_type)
|
214
|
-
end
|
123
|
+
sighash_for_legacy(input_index, output_script, hash_type)
|
215
124
|
end
|
216
125
|
|
217
126
|
# verify input signature.
|
@@ -220,25 +129,15 @@ module Tapyrus
|
|
220
129
|
# @param [Integer] amount the amount of tapyrus, require for witness program only.
|
221
130
|
# @param [Array] flags the flags used when execute script interpreter.
|
222
131
|
def verify_input_sig(input_index, script_pubkey, amount: nil, flags: STANDARD_SCRIPT_VERIFY_FLAGS)
|
223
|
-
script_sig = inputs[input_index].script_sig
|
224
|
-
has_witness = inputs[input_index].has_witness?
|
225
|
-
|
226
132
|
if script_pubkey.p2sh?
|
227
133
|
flags << SCRIPT_VERIFY_P2SH
|
228
|
-
redeem_script = Script.parse_from_payload(script_sig.chunks.last)
|
229
|
-
script_pubkey = redeem_script if redeem_script.p2wpkh?
|
230
|
-
end
|
231
|
-
|
232
|
-
if has_witness || Tapyrus.chain_params.fork_chain?
|
233
|
-
verify_input_sig_for_witness(input_index, script_pubkey, amount, flags)
|
234
|
-
else
|
235
|
-
verify_input_sig_for_legacy(input_index, script_pubkey, flags)
|
236
134
|
end
|
135
|
+
verify_input_sig_for_legacy(input_index, script_pubkey, flags)
|
237
136
|
end
|
238
137
|
|
239
138
|
def to_h
|
240
139
|
{
|
241
|
-
txid: txid, hash:
|
140
|
+
txid: txid, hash: tx_hash, features: features, size: size, locktime: lock_time,
|
242
141
|
vin: inputs.map(&:to_h), vout: outputs.map.with_index{|tx_out, index| tx_out.to_h.merge({n: index})}
|
243
142
|
}
|
244
143
|
end
|
@@ -285,40 +184,12 @@ module Tapyrus
|
|
285
184
|
ins = [ins[index]]
|
286
185
|
end
|
287
186
|
|
288
|
-
buf = [[
|
187
|
+
buf = [[features].pack('V'), Tapyrus.pack_var_int(ins.size),
|
289
188
|
ins, out_size, outs, [lock_time, hash_type].pack('VV')].join
|
290
189
|
|
291
190
|
Tapyrus.double_sha256(buf)
|
292
191
|
end
|
293
192
|
|
294
|
-
# generate sighash with BIP-143 format
|
295
|
-
# https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
|
296
|
-
def sighash_for_witness(index, script_pubkey_or_script_code, hash_type, amount, skip_separator_index)
|
297
|
-
hash_prevouts = Tapyrus.double_sha256(inputs.map{|i|i.out_point.to_payload}.join)
|
298
|
-
hash_sequence = Tapyrus.double_sha256(inputs.map{|i|[i.sequence].pack('V')}.join)
|
299
|
-
outpoint = inputs[index].out_point.to_payload
|
300
|
-
amount = [amount].pack('Q')
|
301
|
-
nsequence = [inputs[index].sequence].pack('V')
|
302
|
-
hash_outputs = Tapyrus.double_sha256(outputs.map{|o|o.to_payload}.join)
|
303
|
-
|
304
|
-
script_code = script_pubkey_or_script_code.to_script_code(skip_separator_index)
|
305
|
-
|
306
|
-
case (hash_type & 0x1f)
|
307
|
-
when SIGHASH_TYPE[:single]
|
308
|
-
hash_outputs = index >= outputs.size ? "\x00".ljust(32, "\x00") : Tapyrus.double_sha256(outputs[index].to_payload)
|
309
|
-
hash_sequence = "\x00".ljust(32, "\x00")
|
310
|
-
when SIGHASH_TYPE[:none]
|
311
|
-
hash_sequence = hash_outputs = "\x00".ljust(32, "\x00")
|
312
|
-
end
|
313
|
-
|
314
|
-
if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
|
315
|
-
hash_prevouts = hash_sequence ="\x00".ljust(32, "\x00")
|
316
|
-
end
|
317
|
-
hash_type |= (Tapyrus.chain_params.fork_id << 8) if Tapyrus.chain_params.fork_chain?
|
318
|
-
buf = [ [version].pack('V'), hash_prevouts, hash_sequence, outpoint,
|
319
|
-
script_code ,amount, nsequence, hash_outputs, [@lock_time, hash_type].pack('VV')].join
|
320
|
-
Tapyrus.double_sha256(buf)
|
321
|
-
end
|
322
193
|
|
323
194
|
# verify input signature for legacy tx.
|
324
195
|
def verify_input_sig_for_legacy(input_index, script_pubkey, flags)
|
@@ -329,19 +200,6 @@ module Tapyrus
|
|
329
200
|
interpreter.verify_script(script_sig, script_pubkey)
|
330
201
|
end
|
331
202
|
|
332
|
-
# verify input signature for witness tx.
|
333
|
-
def verify_input_sig_for_witness(input_index, script_pubkey, amount, flags)
|
334
|
-
flags |= SCRIPT_VERIFY_WITNESS
|
335
|
-
flags |= SCRIPT_VERIFY_WITNESS_PUBKEYTYPE
|
336
|
-
checker = Tapyrus::TxChecker.new(tx: self, input_index: input_index, amount: amount)
|
337
|
-
interpreter = Tapyrus::ScriptInterpreter.new(checker: checker, flags: flags)
|
338
|
-
i = inputs[input_index]
|
339
|
-
|
340
|
-
script_sig = i.script_sig
|
341
|
-
witness = i.script_witness
|
342
|
-
interpreter.verify_script(script_sig, script_pubkey, witness)
|
343
|
-
end
|
344
|
-
|
345
203
|
end
|
346
204
|
|
347
205
|
end
|
data/lib/tapyrus/tx_in.rb
CHANGED
@@ -9,7 +9,6 @@ module Tapyrus
|
|
9
9
|
attr_accessor :out_point
|
10
10
|
attr_accessor :script_sig
|
11
11
|
attr_accessor :sequence
|
12
|
-
attr_accessor :script_witness
|
13
12
|
|
14
13
|
# Setting nSequence to this value for every input in a transaction disables nLockTime.
|
15
14
|
SEQUENCE_FINAL = 0xffffffff
|
@@ -24,10 +23,9 @@ module Tapyrus
|
|
24
23
|
# If TxIn#sequence encodes a relative lock-time, this mask is applied to extract that lock-time from the sequence field.
|
25
24
|
SEQUENCE_LOCKTIME_MASK = 0x0000ffff
|
26
25
|
|
27
|
-
def initialize(out_point: nil, script_sig: Tapyrus::Script.new,
|
26
|
+
def initialize(out_point: nil, script_sig: Tapyrus::Script.new, sequence: SEQUENCE_FINAL)
|
28
27
|
@out_point = out_point
|
29
28
|
@script_sig = script_sig
|
30
|
-
@script_witness = script_witness
|
31
29
|
@sequence = sequence
|
32
30
|
end
|
33
31
|
|
@@ -37,11 +35,10 @@ module Tapyrus
|
|
37
35
|
hash, index = buf.read(36).unpack('a32V')
|
38
36
|
i.out_point = OutPoint.new(hash.bth, index)
|
39
37
|
sig_length = Tapyrus.unpack_var_int_from_io(buf)
|
40
|
-
|
41
|
-
|
42
|
-
i.script_sig.chunks[0] = sig
|
38
|
+
if sig_length == 0
|
39
|
+
i.script_sig = Script.new
|
43
40
|
else
|
44
|
-
i.script_sig = Script.parse_from_payload(
|
41
|
+
i.script_sig = Script.parse_from_payload(buf.read(sig_length))
|
45
42
|
end
|
46
43
|
i.sequence = buf.read(4).unpack('V').first
|
47
44
|
i
|
@@ -61,15 +58,11 @@ module Tapyrus
|
|
61
58
|
p
|
62
59
|
end
|
63
60
|
|
64
|
-
def has_witness?
|
65
|
-
!script_witness.empty?
|
66
|
-
end
|
67
61
|
|
68
62
|
def to_h
|
69
63
|
sig = script_sig.to_h
|
70
64
|
sig.delete(:type)
|
71
65
|
h = {txid: out_point.txid, vout: out_point.index, script_sig: sig }
|
72
|
-
h[:txinwitness] = script_witness.stack.map(&:bth) if has_witness?
|
73
66
|
h[:sequence] = sequence
|
74
67
|
h
|
75
68
|
end
|
data/lib/tapyrus/tx_out.rb
CHANGED
@@ -7,6 +7,7 @@ module Tapyrus
|
|
7
7
|
class TxOut
|
8
8
|
|
9
9
|
include OpenAssets::MarkerOutput
|
10
|
+
include Tapyrus::Color::ColoredOutput
|
10
11
|
|
11
12
|
attr_accessor :value
|
12
13
|
attr_accessor :script_pubkey
|
@@ -62,7 +63,7 @@ module Tapyrus
|
|
62
63
|
def dust_threshold
|
63
64
|
return 0 if script_pubkey.unspendable?
|
64
65
|
n_size = size
|
65
|
-
n_size +=
|
66
|
+
n_size += (32 + 4 + 1 + 107 + 4)
|
66
67
|
fee = n_size * Tapyrus.chain_params.dust_relay_fee / 1000
|
67
68
|
if fee == 0 && n_size != 0
|
68
69
|
fee = Tapyrus.chain_params.dust_relay_fee > 0 ? 1 : -1
|
data/lib/tapyrus/util.rb
CHANGED
data/lib/tapyrus/validation.rb
CHANGED
@@ -13,11 +13,6 @@ module Tapyrus
|
|
13
13
|
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-vout-empty')
|
14
14
|
end
|
15
15
|
|
16
|
-
# Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
|
17
|
-
if tx.serialize_old_format.bytesize * Tapyrus::WITNESS_SCALE_FACTOR > Tapyrus::MAX_BLOCK_WEIGHT
|
18
|
-
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-txns-oversize')
|
19
|
-
end
|
20
|
-
|
21
16
|
# Check for negative or overflow output values
|
22
17
|
amount = 0
|
23
18
|
tx.outputs.each do |o|
|
@@ -34,7 +29,7 @@ module Tapyrus
|
|
34
29
|
end
|
35
30
|
|
36
31
|
if tx.coinbase_tx?
|
37
|
-
if tx.inputs[0].
|
32
|
+
if tx.inputs[0].out_point.index == 0xffffffff || tx.inputs[0].script_sig.size > 100
|
38
33
|
return state.DoS(100, reject_code: Message::Reject::CODE_INVALID, reject_reason: 'bad-cb-length')
|
39
34
|
end
|
40
35
|
else
|