tapyrus 0.1.0 → 0.2.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -14
  3. data/exe/tapyrusrbd +2 -2
  4. data/lib/openassets/util.rb +2 -4
  5. data/lib/schnorr.rb +83 -0
  6. data/lib/schnorr/signature.rb +38 -0
  7. data/lib/tapyrus.rb +9 -11
  8. data/lib/tapyrus/block.rb +1 -32
  9. data/lib/tapyrus/block_header.rb +7 -6
  10. data/lib/tapyrus/chain_params.rb +13 -26
  11. data/lib/tapyrus/chainparams/{testnet.yml → dev.yml} +7 -9
  12. data/lib/tapyrus/chainparams/{mainnet.yml → prod.yml} +7 -10
  13. data/lib/tapyrus/constants.rb +12 -34
  14. data/lib/tapyrus/ext.rb +5 -0
  15. data/lib/tapyrus/ext/json_parser.rb +47 -0
  16. data/lib/tapyrus/ext_key.rb +5 -10
  17. data/lib/tapyrus/key.rb +57 -29
  18. data/lib/tapyrus/message.rb +2 -2
  19. data/lib/tapyrus/message/base.rb +1 -0
  20. data/lib/tapyrus/message/block.rb +3 -3
  21. data/lib/tapyrus/message/cmpct_block.rb +3 -5
  22. data/lib/tapyrus/message/tx.rb +2 -2
  23. data/lib/tapyrus/network/peer.rb +1 -15
  24. data/lib/tapyrus/node/cli.rb +15 -11
  25. data/lib/tapyrus/node/configuration.rb +1 -1
  26. data/lib/tapyrus/node/spv.rb +1 -1
  27. data/lib/tapyrus/opcodes.rb +5 -0
  28. data/lib/tapyrus/out_point.rb +1 -1
  29. data/lib/tapyrus/rpc/request_handler.rb +3 -3
  30. data/lib/tapyrus/rpc/tapyrus_core_client.rb +17 -15
  31. data/lib/tapyrus/script/color.rb +79 -0
  32. data/lib/tapyrus/script/multisig.rb +0 -27
  33. data/lib/tapyrus/script/script.rb +74 -89
  34. data/lib/tapyrus/script/script_error.rb +8 -14
  35. data/lib/tapyrus/script/script_interpreter.rb +65 -86
  36. data/lib/tapyrus/script/tx_checker.rb +16 -4
  37. data/lib/tapyrus/secp256k1.rb +1 -0
  38. data/lib/tapyrus/secp256k1/rfc6979.rb +43 -0
  39. data/lib/tapyrus/secp256k1/ruby.rb +5 -31
  40. data/lib/tapyrus/store/chain_entry.rb +1 -0
  41. data/lib/tapyrus/tx.rb +18 -160
  42. data/lib/tapyrus/tx_in.rb +4 -11
  43. data/lib/tapyrus/tx_out.rb +2 -1
  44. data/lib/tapyrus/util.rb +8 -0
  45. data/lib/tapyrus/validation.rb +1 -6
  46. data/lib/tapyrus/version.rb +1 -1
  47. data/lib/tapyrus/wallet/account.rb +1 -0
  48. data/lib/tapyrus/wallet/master_key.rb +1 -0
  49. data/tapyrusrb.gemspec +3 -3
  50. metadata +42 -39
  51. data/lib/tapyrus/chainparams/regtest.yml +0 -38
  52. data/lib/tapyrus/descriptor.rb +0 -147
  53. data/lib/tapyrus/script_witness.rb +0 -38
@@ -3,6 +3,7 @@ module Tapyrus
3
3
 
4
4
  # wrap a block header object with extra data.
5
5
  class ChainEntry
6
+ include Tapyrus::HexConverter
6
7
 
7
8
  attr_reader :header
8
9
  attr_reader :height
@@ -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
- MARKER = 0x00
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
- @version = 1
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, non_witness: false)
30
+ def self.parse_from_payload(payload)
35
31
  buf = payload.is_a?(String) ? StringIO.new(payload) : payload
36
32
  tx = new
37
- tx.version = buf.read(4).unpack('V').first
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
- to_payload.bth.to_i(16)
52
+ to_hex.to_i(16)
74
53
  end
75
54
 
76
55
  def tx_hash
77
- Tapyrus.double_sha256(serialize_old_format).bth
56
+ Tapyrus.double_sha256(to_payload).bth
78
57
  end
79
58
 
80
59
  def txid
81
- buf = [version].pack('V')
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
- witness? ? serialize_witness_format : serialize_old_format
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
- # serialize tx with segwit tx format
139
- # https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki
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 witness_payload
149
- inputs.map { |i| i.script_witness.to_payload }.join
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 version > MAX_STANDARD_VERSION
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: witness_hash.rhex, version: version, size: size, vsize: vsize, locktime: lock_time,
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 = [[version].pack('V'), Tapyrus.pack_var_int(ins.size),
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
@@ -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, script_witness: ScriptWitness.new, sequence: SEQUENCE_FINAL)
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
- sig = buf.read(sig_length)
41
- if i.coinbase?
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(sig)
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
@@ -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 += script_pubkey.witness_program? ? (32 + 4 + 1 + (107 / Tapyrus::WITNESS_SCALE_FACTOR) + 4) : (32 + 4 + 1 + 107 + 4)
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
@@ -130,4 +130,12 @@ module Tapyrus
130
130
 
131
131
  end
132
132
 
133
+ module HexConverter
134
+
135
+ def to_hex
136
+ to_payload.bth
137
+ end
138
+
139
+ end
140
+
133
141
  end
@@ -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].script_sig.size < 2 || tx.inputs[0].script_sig.size > 100
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
@@ -1,3 +1,3 @@
1
1
  module Tapyrus
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -3,6 +3,7 @@ module Tapyrus
3
3
 
4
4
  # the account in BIP-44
5
5
  class Account
6
+ include Tapyrus::HexConverter
6
7
 
7
8
  PURPOSE_TYPE = {legacy: 44, nested_witness: 49, native_segwit: 84}
8
9
 
@@ -3,6 +3,7 @@ module Tapyrus
3
3
 
4
4
  # HD Wallet master seed
5
5
  class MasterKey
6
+ include Tapyrus::HexConverter
6
7
  extend Tapyrus::Util
7
8
  include Tapyrus::Util
8
9
  include Tapyrus::KeyPath
@@ -23,25 +23,25 @@ Gem::Specification.new do |spec|
23
23
  spec.add_runtime_dependency 'ecdsa'
24
24
  spec.add_runtime_dependency 'eventmachine'
25
25
  spec.add_runtime_dependency 'murmurhash3'
26
- spec.add_runtime_dependency 'bech32', '~> 1.0.3'
27
26
  spec.add_runtime_dependency 'daemon-spawn'
28
27
  spec.add_runtime_dependency 'thor'
29
28
  spec.add_runtime_dependency 'ffi'
30
29
  spec.add_runtime_dependency 'leb128', '~> 1.0.0'
31
30
  spec.add_runtime_dependency 'eventmachine_httpserver'
32
- spec.add_runtime_dependency 'rest-client'
33
31
  spec.add_runtime_dependency 'iniparse'
34
32
  spec.add_runtime_dependency 'siphash'
35
33
  spec.add_runtime_dependency 'protobuf', '3.8.5'
36
34
  spec.add_runtime_dependency 'scrypt'
37
35
  spec.add_runtime_dependency 'activesupport', '>= 5.2.3'
36
+ spec.add_runtime_dependency 'json_pure', '>= 2.3.1'
38
37
 
39
38
  # for options
40
39
  spec.add_development_dependency 'leveldb-native'
41
40
 
42
41
  spec.add_development_dependency 'bundler'
43
- spec.add_development_dependency 'rake', '~> 10.0'
42
+ spec.add_development_dependency 'rake', '>= 12.3.3'
44
43
  spec.add_development_dependency 'rspec', '~> 3.0'
45
44
  spec.add_development_dependency 'timecop'
45
+ spec.add_development_dependency 'webmock', '~> 3.0'
46
46
 
47
47
  end