tapyrus 0.1.0 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -15
  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 +11 -18
  8. data/lib/tapyrus/block.rb +3 -38
  9. data/lib/tapyrus/block_header.rb +70 -41
  10. data/lib/tapyrus/chain_params.rb +13 -36
  11. data/lib/tapyrus/chainparams/{regtest.yml → dev.yml} +10 -19
  12. data/lib/tapyrus/chainparams/{mainnet.yml → prod.yml} +7 -19
  13. data/lib/tapyrus/constants.rb +12 -34
  14. data/lib/tapyrus/errors.rb +17 -0
  15. data/lib/tapyrus/ext.rb +5 -0
  16. data/lib/tapyrus/ext/ecdsa.rb +39 -0
  17. data/lib/tapyrus/ext/json_parser.rb +47 -0
  18. data/lib/tapyrus/ext_key.rb +42 -23
  19. data/lib/tapyrus/key.rb +47 -40
  20. data/lib/tapyrus/message.rb +2 -2
  21. data/lib/tapyrus/message/base.rb +1 -0
  22. data/lib/tapyrus/message/block.rb +4 -4
  23. data/lib/tapyrus/message/cmpct_block.rb +3 -5
  24. data/lib/tapyrus/message/header_and_short_ids.rb +1 -1
  25. data/lib/tapyrus/message/headers.rb +1 -1
  26. data/lib/tapyrus/message/merkle_block.rb +1 -1
  27. data/lib/tapyrus/message/tx.rb +2 -2
  28. data/lib/tapyrus/network/peer.rb +1 -15
  29. data/lib/tapyrus/node/cli.rb +15 -11
  30. data/lib/tapyrus/node/configuration.rb +1 -1
  31. data/lib/tapyrus/node/spv.rb +1 -1
  32. data/lib/tapyrus/opcodes.rb +5 -0
  33. data/lib/tapyrus/out_point.rb +1 -1
  34. data/lib/tapyrus/rpc/request_handler.rb +7 -6
  35. data/lib/tapyrus/rpc/tapyrus_core_client.rb +17 -15
  36. data/lib/tapyrus/script/color.rb +79 -0
  37. data/lib/tapyrus/script/multisig.rb +0 -27
  38. data/lib/tapyrus/script/script.rb +74 -89
  39. data/lib/tapyrus/script/script_error.rb +8 -14
  40. data/lib/tapyrus/script/script_interpreter.rb +65 -86
  41. data/lib/tapyrus/script/tx_checker.rb +21 -5
  42. data/lib/tapyrus/secp256k1.rb +1 -0
  43. data/lib/tapyrus/secp256k1/native.rb +93 -20
  44. data/lib/tapyrus/secp256k1/rfc6979.rb +43 -0
  45. data/lib/tapyrus/secp256k1/ruby.rb +63 -54
  46. data/lib/tapyrus/store/chain_entry.rb +3 -2
  47. data/lib/tapyrus/store/db/level_db.rb +58 -0
  48. data/lib/tapyrus/store/spv_chain.rb +29 -11
  49. data/lib/tapyrus/tx.rb +18 -160
  50. data/lib/tapyrus/tx_in.rb +4 -11
  51. data/lib/tapyrus/tx_out.rb +2 -1
  52. data/lib/tapyrus/util.rb +8 -0
  53. data/lib/tapyrus/validation.rb +1 -6
  54. data/lib/tapyrus/version.rb +1 -1
  55. data/lib/tapyrus/wallet/account.rb +1 -0
  56. data/lib/tapyrus/wallet/master_key.rb +1 -0
  57. data/tapyrusrb.gemspec +3 -3
  58. metadata +44 -39
  59. data/lib/tapyrus/chainparams/testnet.yml +0 -41
  60. data/lib/tapyrus/descriptor.rb +0 -147
  61. data/lib/tapyrus/script_witness.rb +0 -38
@@ -0,0 +1,79 @@
1
+ module Tapyrus
2
+ module Color
3
+ module TokenTypes
4
+ NONE = 0x00
5
+ REISSUABLE = 0xC1
6
+ NON_REISSUABLE = 0xC2
7
+ NFT = 0xC3
8
+ end
9
+
10
+ class ColorIdentifier
11
+ include Tapyrus::HexConverter
12
+
13
+ attr_reader :type, :payload
14
+
15
+ def self.reissuable(script_pubkey)
16
+ new(TokenTypes::REISSUABLE, Tapyrus.sha256(script_pubkey.to_payload))
17
+ end
18
+
19
+ def self.non_reissuable(out_point)
20
+ new(TokenTypes::NON_REISSUABLE, Tapyrus.sha256(out_point.to_payload))
21
+ end
22
+
23
+ def self.nft(out_point)
24
+ new(TokenTypes::NFT, Tapyrus.sha256(out_point.to_payload))
25
+ end
26
+
27
+ def to_payload
28
+ [type, payload].pack('Ca*')
29
+ end
30
+
31
+ def self.parse_from_payload(payload)
32
+ type, payload = payload.unpack('Ca*')
33
+ new(type, payload)
34
+ end
35
+
36
+ def ==(other)
37
+ other && other.to_payload == to_payload
38
+ end
39
+
40
+ def valid?
41
+ return false unless [TokenTypes::REISSUABLE, TokenTypes::NON_REISSUABLE, TokenTypes::NFT].include?(type)
42
+ return false unless payload.bytesize == 32
43
+ true
44
+ end
45
+
46
+ private
47
+
48
+ def initialize(type, payload)
49
+ @type = type
50
+ @payload = payload
51
+ end
52
+ end
53
+
54
+ module ColoredOutput
55
+ def colored?
56
+ script_pubkey.cp2pkh? || script_pubkey.cp2sh?
57
+ end
58
+
59
+ def color_id
60
+ @color_id ||= ColorIdentifier.parse_from_payload(script_pubkey.chunks[0].pushed_data)
61
+ end
62
+
63
+ def reissuable?
64
+ return false unless colored?
65
+ color_id.type == TokenTypes::REISSUABLE
66
+ end
67
+
68
+ def non_reissuable?
69
+ return false unless colored?
70
+ color_id.type == TokenTypes::NON_REISSUABLE
71
+ end
72
+
73
+ def nft?
74
+ return false unless colored?
75
+ color_id.type == TokenTypes::NFT
76
+ end
77
+ end
78
+ end
79
+ end
@@ -61,32 +61,5 @@ module Tapyrus
61
61
  prefix + pubkeys.map { |k| sigs[k] ? Tapyrus::Script.pack_pushdata(sigs[k]) : nil }.join +
62
62
  Tapyrus::Script.pack_pushdata(script.chunks[-1].pushed_data)
63
63
  end
64
-
65
- def self.add_sig_to_multisig_script_witness(sig_to_add, script_witness, hash_type = SIGHASH_TYPE[:all])
66
- signature = sig_to_add + [hash_type].pack("C*")
67
- script_witness.stack << signature
68
- end
69
-
70
- # Sort signatures in the given +script_witness+ according to the order of pubkeys in
71
- # the redeem script. Also needs the +sig_hash+ to match signatures to pubkeys.
72
- # @param [ScriptWitness] script_witness for multisig.
73
- # @param [String] sig_hash to be signed.
74
- def self.sort_witness_multisig_signatures(script_witness, sig_hash)
75
- redeem_script = Tapyrus::Script.parse_from_payload(script_witness.stack[-1])
76
- pubkeys = redeem_script.get_multisig_pubkeys
77
- sigs = Hash[script_witness.stack[1...-1].map.with_index do |sig, idx|
78
- pubkey = pubkeys.map do |key|
79
- Tapyrus::Key.new(pubkey: key.bth).verify(sig, sig_hash) ? key : nil
80
- end.compact.first
81
- raise "Key for signature ##{idx} not found in redeem script!" unless pubkey
82
- [pubkey, sig]
83
- end]
84
- script_witness.stack.clear
85
- script_witness.stack << ''
86
- pubkeys.each do |pubkey|
87
- script_witness.stack << sigs[pubkey] if sigs[pubkey]
88
- end
89
- script_witness.stack << redeem_script.to_payload
90
- end
91
64
  end
92
65
  end
@@ -5,6 +5,7 @@ module Tapyrus
5
5
 
6
6
  # tapyrus script
7
7
  class Script
8
+ include Tapyrus::HexConverter
8
9
  include Tapyrus::Opcodes
9
10
 
10
11
  attr_accessor :chunks
@@ -18,11 +19,6 @@ module Tapyrus
18
19
  new << OP_DUP << OP_HASH160 << pubkey_hash << OP_EQUALVERIFY << OP_CHECKSIG
19
20
  end
20
21
 
21
- # generate P2WPKH script
22
- def self.to_p2wpkh(pubkey_hash)
23
- new << WITNESS_VERSION << pubkey_hash
24
- end
25
-
26
22
  # generate m of n multisig p2sh script
27
23
  # @param [String] m the number of signatures required for multisig
28
24
  # @param [Array] pubkeys array of public keys that compose multisig
@@ -45,6 +41,40 @@ module Tapyrus
45
41
  Script.to_p2sh(to_hash160)
46
42
  end
47
43
 
44
+ # generate cp2pkh script
45
+ # @param [ColorIdentifier] color identifier
46
+ # @param [String] hash160 of pubkey
47
+ # @return [Script] CP2PKH script
48
+ # @raise [ArgumentError] if color_id is nil or invalid
49
+ def self.to_cp2pkh(color_id, pubkey_hash)
50
+ raise ArgumentError, 'Specified color identifier is invalid' unless color_id&.valid?
51
+ new << color_id.to_payload << OP_COLOR << OP_DUP << OP_HASH160 << pubkey_hash << OP_EQUALVERIFY << OP_CHECKSIG
52
+ end
53
+
54
+ # generate cp2sh script
55
+ # @param [ColorIdentifier] color identifier
56
+ # @param [String] hash160 of script
57
+ # @return [Script] CP2SH script
58
+ # @raise [ArgumentError] if color_id is nil or invalid
59
+ def self.to_cp2sh(color_id, script_hash)
60
+ raise ArgumentError, 'Specified color identifier is invalid' unless color_id&.valid?
61
+ new << color_id.to_payload << OP_COLOR << OP_HASH160 << script_hash << OP_EQUAL
62
+ end
63
+
64
+ # Add color identifier to existing p2pkh or p2sh
65
+ # @param [ColorIdentifier] color identifier
66
+ # @return [Script] CP2PKH or CP2SH script
67
+ # @raise [ArgumentError] if color_id is nil or invalid
68
+ # @raise [RuntimeError] if script is neither p2pkh nor p2sh
69
+ def add_color(color_id)
70
+ raise ArgumentError, 'Specified color identifier is invalid' unless color_id&.valid?
71
+ raise RuntimeError, 'Only p2pkh and p2sh can add color' unless p2pkh? or p2sh?
72
+ Tapyrus::Script.new.tap do |s|
73
+ s << color_id.to_payload << OP_COLOR
74
+ s.chunks += self.chunks
75
+ end
76
+ end
77
+
48
78
  def get_multisig_pubkeys
49
79
  num = Tapyrus::Opcodes.opcode_to_small_int(chunks[-2].bth.to_i(16))
50
80
  (1..num).map{ |i| chunks[i].pushed_data }
@@ -59,13 +89,6 @@ module Tapyrus
59
89
  new << m << pubkeys << pubkeys.size << OP_CHECKMULTISIG
60
90
  end
61
91
 
62
- # generate p2wsh script for +redeem_script+
63
- # @param [Script] redeem_script target redeem script
64
- # @param [Script] p2wsh script
65
- def self.to_p2wsh(redeem_script)
66
- new << WITNESS_VERSION << redeem_script.to_sha256
67
- end
68
-
69
92
  # generate script from string.
70
93
  def self.from_string(string)
71
94
  script = new
@@ -151,14 +174,15 @@ module Tapyrus
151
174
  def addresses
152
175
  return [p2pkh_addr] if p2pkh?
153
176
  return [p2sh_addr] if p2sh?
154
- return [bech32_addr] if witness_program?
177
+ return [cp2pkh_addr] if cp2pkh?
178
+ return [cp2sh_addr] if cp2sh?
155
179
  return get_multisig_pubkeys.map{|pubkey| Tapyrus::Key.new(pubkey: pubkey.bth).to_p2pkh} if multisig?
156
180
  []
157
181
  end
158
182
 
159
183
  # check whether standard script.
160
184
  def standard?
161
- p2pkh? | p2sh? | p2wpkh? | p2wsh? | multisig? | standard_op_return?
185
+ p2pkh? | p2sh? | multisig? | standard_op_return?
162
186
  end
163
187
 
164
188
  # whether this script is a P2PKH format script.
@@ -168,17 +192,6 @@ module Tapyrus
168
192
  (chunks[0..1]+ chunks[3..4]).map(&:ord) && chunks[2].bytesize == 21
169
193
  end
170
194
 
171
- # whether this script is a P2WPKH format script.
172
- def p2wpkh?
173
- return false unless chunks.size == 2
174
- chunks[0].ord == WITNESS_VERSION && chunks[1].bytesize == 21
175
- end
176
-
177
- def p2wsh?
178
- return false unless chunks.size == 2
179
- chunks[0].ord == WITNESS_VERSION && chunks[1].bytesize == 33
180
- end
181
-
182
195
  def p2sh?
183
196
  return false unless chunks.size == 3
184
197
  OP_HASH160 == chunks[0].ord && OP_EQUAL == chunks[2].ord && chunks[1].bytesize == 21
@@ -201,6 +214,23 @@ module Tapyrus
201
214
  (chunks.size == 1 || chunks[1].opcode <= OP_16)
202
215
  end
203
216
 
217
+ def cp2pkh?
218
+ return false unless chunks.size == 7
219
+ return false unless chunks[0].bytesize == 34
220
+ return false unless Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)&.valid?
221
+ return false unless chunks[1].ord == OP_COLOR
222
+ [OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] ==
223
+ (chunks[2..3]+ chunks[5..6]).map(&:ord) && chunks[4].bytesize == 21
224
+ end
225
+
226
+ def cp2sh?
227
+ return false unless chunks.size == 5
228
+ return false unless chunks[0].bytesize == 34
229
+ return false unless Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)&.valid?
230
+ return false unless chunks[1].ord == OP_COLOR
231
+ OP_HASH160 == chunks[2].ord && OP_EQUAL == chunks[4].ord && chunks[3].bytesize == 21
232
+ end
233
+
204
234
  def op_return_data
205
235
  return nil unless op_return?
206
236
  return nil if chunks.size == 1
@@ -221,50 +251,15 @@ module Tapyrus
221
251
  chunks.select{|c|c.pushdata? && [33, 65].include?(c.pushed_data.bytesize) && [2, 3, 4, 6, 7].include?(c.pushed_data[0].bth.to_i(16))}.map{|c|c.pushed_data.bth}
222
252
  end
223
253
 
224
- # A witness program is any valid Script that consists of a 1-byte push opcode followed by a data push between 2 and 40 bytes.
225
- def witness_program?
226
- return false if size < 4 || size > 42 || chunks.size < 2
227
-
228
- opcode = chunks[0].opcode
229
-
230
- return false if opcode != OP_0 && (opcode < OP_1 || opcode > OP_16)
231
- return false unless chunks[1].pushdata?
232
-
233
- if size == (chunks[1][0].unpack('C').first + 2)
234
- program_size = chunks[1].pushed_data.bytesize
235
- return program_size >= 2 && program_size <= 40
236
- end
237
-
238
- false
239
- end
240
-
241
- # get witness commitment
242
- def witness_commitment
243
- return nil if !op_return? || op_return_data.bytesize < 36
244
- buf = StringIO.new(op_return_data)
245
- return nil unless buf.read(4).bth == WITNESS_COMMITMENT_HEADER
246
- buf.read(32).bth
247
- end
248
-
249
- # If this script is witness program, return its script code,
250
- # otherwise returns the self payload. ScriptInterpreter does not use this.
254
+ # returns the self payload. ScriptInterpreter does not use this.
251
255
  def to_script_code(skip_separator_index = 0)
252
256
  payload = to_payload
253
- if p2wpkh?
254
- payload = Script.to_p2pkh(chunks[1].pushed_data.bth).to_payload
255
- elsif skip_separator_index > 0
257
+ if skip_separator_index > 0
256
258
  payload = subscript_codeseparator(skip_separator_index)
257
259
  end
258
260
  Tapyrus.pack_var_string(payload)
259
261
  end
260
262
 
261
- # get witness version and witness program
262
- def witness_data
263
- version = opcode_to_small_int(chunks[0].opcode)
264
- program = chunks[1].pushed_data
265
- [version, program]
266
- end
267
-
268
263
  # append object to payload
269
264
  def <<(obj)
270
265
  if obj.is_a?(Integer)
@@ -324,6 +319,7 @@ module Tapyrus
324
319
  when Integer
325
320
  opcode_to_name(c)
326
321
  when String
322
+ return c if c.empty?
327
323
  if c.pushdata?
328
324
  v = Opcodes.opcode_to_small_int(c.ord)
329
325
  if v
@@ -351,7 +347,7 @@ module Tapyrus
351
347
 
352
348
  # generate hash160 hash for payload
353
349
  def to_hash160
354
- Tapyrus.hash160(to_payload.bth)
350
+ Tapyrus.hash160(to_hex)
355
351
  end
356
352
 
357
353
  # script size
@@ -482,13 +478,11 @@ module Tapyrus
482
478
  return 'pubkeyhash' if p2pkh?
483
479
  return 'scripthash' if p2sh?
484
480
  return 'multisig' if multisig?
485
- return 'witness_v0_keyhash' if p2wpkh?
486
- return 'witness_v0_scripthash' if p2wsh?
487
481
  'nonstandard'
488
482
  end
489
483
 
490
484
  def to_h
491
- h = {asm: to_s, hex: to_payload.bth, type: type}
485
+ h = {asm: to_s, hex: to_hex, type: type}
492
486
  addrs = addresses
493
487
  unless addrs.empty?
494
488
  h[:req_sigs] = multisig? ? Tapyrus::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) :addrs.size
@@ -504,48 +498,39 @@ module Tapyrus
504
498
  (size > 0 && op_return?) || size > Tapyrus::MAX_SCRIPT_SIZE
505
499
  end
506
500
 
507
- # convert payload to hex data.
508
- # @return [String] script with hex format.
509
- def to_hex
510
- to_payload.bth
511
- end
512
-
513
501
  private
514
502
 
515
503
  # generate p2pkh address. if script dose not p2pkh, return nil.
516
504
  def p2pkh_addr
517
505
  return nil unless p2pkh?
518
506
  hash160 = chunks[2].pushed_data.bth
519
- return nil unless hash160.htb.bytesize == 20
520
507
  Tapyrus.encode_base58_address(hash160, Tapyrus.chain_params.address_version)
521
508
  end
522
509
 
523
- # generate p2wpkh address. if script dose not p2wpkh, return nil.
524
- def p2wpkh_addr
525
- p2wpkh? ? bech32_addr : nil
526
- end
527
-
528
510
  # generate p2sh address. if script dose not p2sh, return nil.
529
511
  def p2sh_addr
530
512
  return nil unless p2sh?
531
513
  hash160 = chunks[1].pushed_data.bth
532
- return nil unless hash160.htb.bytesize == 20
533
514
  Tapyrus.encode_base58_address(hash160, Tapyrus.chain_params.p2sh_version)
534
515
  end
535
516
 
536
- # generate p2wsh address. if script dose not p2wsh, return nil.
537
- def p2wsh_addr
538
- p2wsh? ? bech32_addr : nil
539
- end
517
+ # generate cp2pkh address. if script dose not cp2pkh, return nil.
518
+ def cp2pkh_addr
519
+ return nil unless cp2pkh?
540
520
 
541
- # return bech32 address for payload
542
- def bech32_addr
543
- segwit_addr = Bech32::SegwitAddr.new
544
- segwit_addr.hrp = Tapyrus.chain_params.bech32_hrp
545
- segwit_addr.script_pubkey = to_payload.bth
546
- segwit_addr.addr
521
+ color_id = chunks[0].pushed_data.bth
522
+ hash160 = chunks[4].pushed_data.bth
523
+ Tapyrus.encode_base58_address(color_id + hash160, Tapyrus.chain_params.cp2pkh_version)
547
524
  end
548
525
 
526
+ # generate cp2sh address. if script dose not cp2sh, return nil.
527
+ def cp2sh_addr
528
+ return nil unless cp2sh?
529
+
530
+ color_id = chunks[0].pushed_data.bth
531
+ hash160 = chunks[3].pushed_data.bth
532
+ Tapyrus.encode_base58_address(color_id + hash160, Tapyrus.chain_params.cp2sh_version)
533
+ end
549
534
  end
550
535
 
551
536
  end
@@ -78,24 +78,18 @@ module Tapyrus
78
78
  'Witness version reserved for soft-fork upgrades'
79
79
  when SCRIPT_ERR_PUBKEYTYPE
80
80
  'Public key is neither compressed or uncompressed'
81
- when SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH
82
- 'Witness program has incorrect length'
83
- when SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY
84
- 'Witness program was passed an empty witness'
85
- when SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH
86
- 'Witness program hash mismatch'
87
- when SCRIPT_ERR_WITNESS_MALLEATED
88
- 'Witness requires empty scriptSig'
89
- when SCRIPT_ERR_WITNESS_MALLEATED_P2SH
90
- 'Witness requires only-redeemscript scriptSig'
91
- when SCRIPT_ERR_WITNESS_UNEXPECTED
92
- 'Witness provided for non-witness script'
93
- when SCRIPT_ERR_WITNESS_PUBKEYTYPE
94
- 'Using non-compressed keys in segwit'
95
81
  when SCRIPT_ERR_OP_CODESEPARATOR
96
82
  'Using OP_CODESEPARATOR in non-witness scrip'
97
83
  when SCRIPT_ERR_SIG_FINDANDDELETE
98
84
  'Signature is found in scriptCode'
85
+ when SCRIPT_ERR_OP_COLOR_UNEXPECTED
86
+ 'Unexpected OP_COLOR in script'
87
+ when SCRIPT_ERR_OP_COLOR_ID_INVALID
88
+ 'Invalid ColorId in script'
89
+ when SCRIPT_ERR_OP_COLOR_MULTIPLE
90
+ 'Multiple OP_COLOR found in script'
91
+ when SCRIPT_ERR_OP_COLOR_IN_BRANCH
92
+ 'OP_COLOR is not permitted in a script branch'
99
93
  when SCRIPT_ERR_UNKNOWN_ERROR, SCRIPT_ERR_ERROR_COUNT
100
94
  'unknown error'
101
95
  else
@@ -32,32 +32,21 @@ module Tapyrus
32
32
  # eval script
33
33
  # @param [Tapyrus::Script] script_sig a signature script (unlock script which data push only)
34
34
  # @param [Tapyrus::Script] script_pubkey a script pubkey (locking script)
35
- # @param [Tapyrus::ScriptWitness] witness a witness script
36
35
  # @return [Boolean] result
37
- def verify_script(script_sig, script_pubkey, witness = ScriptWitness.new)
36
+ def verify_script(script_sig, script_pubkey)
38
37
 
39
38
  return set_error(SCRIPT_ERR_SIG_PUSHONLY) if flag?(SCRIPT_VERIFY_SIGPUSHONLY) && !script_sig.push_only?
40
39
 
41
40
  stack_copy = nil
42
- had_witness = false
43
41
 
44
- return false unless eval_script(script_sig, :base)
42
+ return false unless eval_script(script_sig, :base, false)
45
43
 
46
44
  stack_copy = stack.dup if flag?(SCRIPT_VERIFY_P2SH)
47
45
 
48
- return false unless eval_script(script_pubkey, :base)
46
+ return false unless eval_script(script_pubkey, :base, false)
49
47
 
50
48
  return set_error(SCRIPT_ERR_EVAL_FALSE) if stack.empty? || !cast_to_bool(stack.last.htb)
51
49
 
52
- # Bare witness programs
53
- if flag?(SCRIPT_VERIFY_WITNESS) && script_pubkey.witness_program?
54
- had_witness = true
55
- return set_error(SCRIPT_ERR_WITNESS_MALLEATED) unless script_sig.size == 0
56
- version, program = script_pubkey.witness_data
57
- stack_copy = stack.dup
58
- return false unless verify_witness_program(witness, version, program)
59
- end
60
-
61
50
  # Additional validation for spend-to-script-hash transactions
62
51
  if flag?(SCRIPT_VERIFY_P2SH) && script_pubkey.p2sh?
63
52
  return set_error(SCRIPT_ERR_SIG_PUSHONLY) unless script_sig.push_only?
@@ -69,18 +58,8 @@ module Tapyrus
69
58
  rescue Exception => e
70
59
  return set_error(SCRIPT_ERR_BAD_OPCODE, "Failed to parse serialized redeem script for P2SH. #{e.message}")
71
60
  end
72
- return false unless eval_script(redeem_script, :base)
61
+ return false unless eval_script(redeem_script, :base, true)
73
62
  return set_error(SCRIPT_ERR_EVAL_FALSE) if stack.empty? || !cast_to_bool(stack.last)
74
-
75
- # P2SH witness program
76
- if flag?(SCRIPT_VERIFY_WITNESS) && redeem_script.witness_program?
77
- had_witness = true
78
- # The scriptSig must be _exactly_ a single push of the redeemScript. Otherwise we reintroduce malleability.
79
- return set_error(SCRIPT_ERR_WITNESS_MALLEATED_P2SH) unless script_sig == (Tapyrus::Script.new << redeem_script.to_payload.bth)
80
-
81
- version, program = redeem_script.witness_data
82
- return false unless verify_witness_program(witness, version, program)
83
- end
84
63
  end
85
64
 
86
65
  # The CLEANSTACK check is only performed after potential P2SH evaluation,
@@ -93,11 +72,6 @@ module Tapyrus
93
72
  return set_error(SCRIPT_ERR_CLEANSTACK) unless stack.size == 1
94
73
  end
95
74
 
96
- if flag?(SCRIPT_VERIFY_WITNESS)
97
- raise 'assert' unless flag?(SCRIPT_VERIFY_P2SH)
98
- return set_error(SCRIPT_ERR_WITNESS_UNEXPECTED) if !had_witness && !witness.empty?
99
- end
100
-
101
75
  true
102
76
  end
103
77
 
@@ -106,45 +80,14 @@ module Tapyrus
106
80
  false
107
81
  end
108
82
 
109
- def verify_witness_program(witness, version, program)
110
- if version == 0
111
- if program.bytesize == 32
112
- return set_error(SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY) if witness.stack.size == 0
113
- script_pubkey = Tapyrus::Script.parse_from_payload(witness.stack.last)
114
- @stack = witness.stack[0..-2].map{|w|w.bth}
115
- script_hash = Tapyrus.sha256(script_pubkey.to_payload)
116
- return set_error(SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH) unless script_hash == program
117
- elsif program.bytesize == 20
118
- return set_error(SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH) unless witness.stack.size == 2
119
- script_pubkey = Tapyrus::Script.to_p2pkh(program.bth)
120
- @stack = witness.stack.map{|w|w.bth}
121
- else
122
- return set_error(SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH)
123
- end
124
- elsif flag?(SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
125
- return set_error(SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)
126
- else
127
- return true # Higher version witness scripts return true for future softfork compatibility
128
- end
129
-
130
- stack.each do |s| # Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack
131
- return set_error(SCRIPT_ERR_PUSH_SIZE) if s.htb.bytesize > MAX_SCRIPT_ELEMENT_SIZE
132
- end
133
-
134
- return false unless eval_script(script_pubkey, :witness_v0)
135
-
136
- return set_error(SCRIPT_ERR_EVAL_FALSE) unless stack.size == 1
137
- return set_error(SCRIPT_ERR_EVAL_FALSE) unless cast_to_bool(stack.last)
138
- true
139
- end
140
-
141
- def eval_script(script, sig_version)
83
+ def eval_script(script, sig_version, is_redeem_script)
142
84
  return set_error(SCRIPT_ERR_SCRIPT_SIZE) if script.size > MAX_SCRIPT_SIZE
143
85
  begin
144
86
  flow_stack = []
145
87
  alt_stack = []
146
88
  last_code_separator_index = 0
147
89
  op_count = 0
90
+ color_id = nil
148
91
 
149
92
  script.chunks.each_with_index do |c, index|
150
93
  need_exec = !flow_stack.include?(false)
@@ -209,7 +152,7 @@ module Tapyrus
209
152
  if need_exec
210
153
  return set_error(SCRIPT_ERR_UNBALANCED_CONDITIONAL) if stack.size < 1
211
154
  value = pop_string.htb
212
- if sig_version == :witness_v0 && flag?(SCRIPT_VERIFY_MINIMALIF)
155
+ if flag?(SCRIPT_VERIFY_MINIMALIF)
213
156
  if value.bytesize > 1 || (value.bytesize == 1 && value[0].unpack('C').first != 1)
214
157
  return set_error(SCRIPT_ERR_MINIMALIF)
215
158
  end
@@ -409,8 +352,8 @@ module Tapyrus
409
352
  return set_error(SCRIPT_ERR_SIG_FINDANDDELETE) if flag?(SCRIPT_VERIFY_CONST_SCRIPTCODE) && tmp != subscript
410
353
  subscript = tmp
411
354
  end
412
-
413
- return false if !check_pubkey_encoding(pubkey, sig_version) || !check_signature_encoding(sig) # error already set.
355
+ return false if (sig.htb.bytesize == Tapyrus::Key::COMPACT_SIGNATURE_SIZE ?
356
+ !check_schnorr_signature_encoding(sig) : !check_ecdsa_signature_encoding(sig)) || !check_pubkey_encoding(pubkey)
414
357
 
415
358
  success = checker.check_sig(sig, pubkey, subscript, sig_version)
416
359
 
@@ -428,6 +371,19 @@ module Tapyrus
428
371
  return set_error(SCRIPT_ERR_CHECKSIGVERIFY)
429
372
  end
430
373
  end
374
+ when OP_CHECKDATASIG, OP_CHECKDATASIGVERIFY
375
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 3
376
+ sig, msg, pubkey = pop_string(3)
377
+ # check signature encoding without hashtype byte
378
+ return false if (sig.htb.bytesize != (Tapyrus::Key::COMPACT_SIGNATURE_SIZE - 1) && !check_ecdsa_signature_encoding(sig, true)) || !check_pubkey_encoding(pubkey)
379
+ digest = Tapyrus.sha256(msg)
380
+ success = checker.verify_sig(sig, pubkey, digest)
381
+ return set_error(SCRIPT_ERR_SIG_NULLFAIL) if !success && flag?(SCRIPT_VERIFY_NULLFAIL) && sig.bytesize > 0
382
+ push_int(success ? 1 : 0)
383
+ if opcode == OP_CHECKDATASIGVERIFY
384
+ stack.pop if success
385
+ return set_error(SCRIPT_ERR_CHECKDATASIGVERIFY) unless success
386
+ end
431
387
  when OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY
432
388
  return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
433
389
  pubkey_count = pop_int
@@ -463,10 +419,17 @@ module Tapyrus
463
419
  end
464
420
 
465
421
  success = true
422
+ current_sig_scheme = nil
466
423
  while success && sig_count > 0
467
424
  sig = sigs.pop
468
425
  pubkey = pubkeys.pop
469
- return false if !check_pubkey_encoding(pubkey, sig_version) || !check_signature_encoding(sig) # error already set.
426
+ sig_scheme = sig.htb.bytesize == Tapyrus::Key::COMPACT_SIGNATURE_SIZE ? :schnorr : :ecdsa
427
+ current_sig_scheme = sig_scheme if current_sig_scheme.nil?
428
+
429
+ return false if (sig_scheme == :schnorr ? !check_schnorr_signature_encoding(sig) : !check_ecdsa_signature_encoding(sig)) || !check_pubkey_encoding(pubkey) # error already set.
430
+
431
+ return set_error(SCRIPT_ERR_MIXED_SCHEME_MULTISIG) unless sig_scheme == current_sig_scheme
432
+
470
433
  ok = checker.check_sig(sig, pubkey, subscript, sig_version)
471
434
  if ok
472
435
  sig_count -= 1
@@ -488,9 +451,8 @@ module Tapyrus
488
451
  # Unfortunately this is a potential source of mutability,
489
452
  # so optionally verify it is exactly equal to zero prior to removing it from the stack.
490
453
  return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size < 1
491
- if flag?(SCRIPT_VERIFY_NULLDUMMY) && stack[-1].size > 0
492
- return set_error(SCRIPT_ERR_SIG_NULLDUMMY)
493
- end
454
+ return set_error(SCRIPT_ERR_SIG_NULLDUMMY) if stack[-1].size > 0
455
+
494
456
  stack.pop
495
457
 
496
458
  push_int(success ? 1 : 0)
@@ -503,6 +465,25 @@ module Tapyrus
503
465
  end
504
466
  when OP_RETURN
505
467
  return set_error(SCRIPT_ERR_OP_RETURN)
468
+ when OP_COLOR
469
+ # Color id is not permitted in p2sh redeem script
470
+ return set_error(SCRIPT_ERR_OP_COLOR_UNEXPECTED) if is_redeem_script
471
+
472
+ # if Color id is already initialized this must be an extra
473
+ return set_error(SCRIPT_ERR_OP_COLOR_MULTIPLE) if color_id && color_id.type != Tapyrus::Color::TokenTypes::NONE
474
+
475
+ # color id is not allowed inside OP_IF
476
+ return set_error(SCRIPT_ERR_OP_COLOR_IN_BRANCH) unless flow_stack.empty?
477
+
478
+ # pop one stack element and verify that it exists
479
+ return set_error(SCRIPT_ERR_INVALID_STACK_OPERATION) if stack.size() < 1
480
+
481
+ color_id = Tapyrus::Color::ColorIdentifier.parse_from_payload(stack.last.htb)
482
+
483
+ # check ColorIdentifier is valid
484
+ return set_error(SCRIPT_ERR_OP_COLOR_ID_INVALID) unless color_id.valid?
485
+
486
+ stack.pop
506
487
  else
507
488
  return set_error(SCRIPT_ERR_BAD_OPCODE)
508
489
  end
@@ -586,20 +567,26 @@ module Tapyrus
586
567
  end
587
568
  end
588
569
 
589
- def check_signature_encoding(sig)
570
+ def check_ecdsa_signature_encoding(sig, data_sig = false)
590
571
  return true if sig.size.zero?
591
- if (flag?(SCRIPT_VERIFY_DERSIG) || flag?(SCRIPT_VERIFY_LOW_S) || flag?(SCRIPT_VERIFY_STRICTENC)) && !Key.valid_signature_encoding?(sig.htb)
572
+ if !Key.valid_signature_encoding?(sig.htb, data_sig)
592
573
  return set_error(SCRIPT_ERR_SIG_DER)
593
- elsif flag?(SCRIPT_VERIFY_LOW_S) && !low_der_signature?(sig)
574
+ elsif !low_der_signature?(sig, data_sig)
594
575
  return false
595
- elsif flag?(SCRIPT_VERIFY_STRICTENC) && !defined_hashtype_signature?(sig)
576
+ elsif !data_sig && !defined_hashtype_signature?(sig)
596
577
  return set_error(SCRIPT_ERR_SIG_HASHTYPE)
597
578
  end
598
579
  true
599
580
  end
600
581
 
601
- def low_der_signature?(sig)
602
- return set_error(SCRIPT_ERR_SIG_DER) unless Key.valid_signature_encoding?(sig.htb)
582
+ def check_schnorr_signature_encoding(sig, data_sig = false)
583
+ return false unless sig.htb.bytesize == (data_sig ? 64 : 65)
584
+ return set_error(SCRIPT_ERR_SIG_HASHTYPE) if !data_sig && !defined_hashtype_signature?(sig)
585
+ true
586
+ end
587
+
588
+ def low_der_signature?(sig, data_sig = false)
589
+ return set_error(SCRIPT_ERR_SIG_DER) unless Key.valid_signature_encoding?(sig.htb, data_sig)
603
590
  return set_error(SCRIPT_ERR_SIG_HIGH_S) unless Key.low_signature?(sig.htb)
604
591
  true
605
592
  end
@@ -609,20 +596,12 @@ module Tapyrus
609
596
  return false if sig.empty?
610
597
  s = sig.unpack('C*')
611
598
  hash_type = s[-1] & (~(SIGHASH_TYPE[:anyonecanpay]))
612
- hash_type &= (~(Tapyrus::SIGHASH_FORK_ID)) if Tapyrus.chain_params.fork_chain? # for fork coin.
613
599
  return false if hash_type < SIGHASH_TYPE[:all] || hash_type > SIGHASH_TYPE[:single]
614
600
  true
615
601
  end
616
602
 
617
- def check_pubkey_encoding(pubkey, sig_version)
618
- if flag?(SCRIPT_VERIFY_STRICTENC) && !Key.compress_or_uncompress_pubkey?(pubkey)
619
- return set_error(SCRIPT_ERR_PUBKEYTYPE)
620
- end
621
- # Only compressed keys are accepted in segwit
622
- if flag?(SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) &&
623
- sig_version == :witness_v0 && !Key.compress_pubkey?(pubkey)
624
- return set_error(SCRIPT_ERR_WITNESS_PUBKEYTYPE)
625
- end
603
+ def check_pubkey_encoding(pubkey)
604
+ return set_error(SCRIPT_ERR_PUBKEYTYPE) unless Key.compress_or_uncompress_pubkey?(pubkey)
626
605
  true
627
606
  end
628
607