tapyrus 0.3.2 → 0.3.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfdff40e4807919098065d5d956383663673f20a264d0eaadaa89124467166f8
4
- data.tar.gz: 731e768f37f843fc6a03d337e8aea349d57f23449068356348d2bef171d02a2f
3
+ metadata.gz: 8b6715191d7f10eb9d2d6851660e71abcf1aa9866b2568d15774faa22af6e31d
4
+ data.tar.gz: e224860705d15fe929011d56db6d1ea959f13eadb32dcb915c859db5751ddf00
5
5
  SHA512:
6
- metadata.gz: 0360ede886fe0a3512ea68e212270b5d931b8c085ceb04c6790978e325bc98b1d112d74deaa80232c97109c50ad8b1175566cb04ccd5324a218563677d90fbbd
7
- data.tar.gz: 3f6f38a994e81c0b1d5157584cf14669ee7f8be612a6e878bf7cc216995ca60255a133b1b318e6210d6328015fdd99434bde021fd124bdc44454da564023ddab
6
+ metadata.gz: b96b08953e986ad0db52c4885df2315ee0c69748e19e9486c80898886ec74f763158b71dc972881f7831a099f91f896ba21d3d04552d258dcbc6057d9d56f08a
7
+ data.tar.gz: 84a9558443d75e15768d4f485dbff148fb8ac4014574e41a49050197f4d0eb18dfb5a3b5f0d3945d56a7da53c056c2db84a722ed9aed1b25df9da62be2f98aac
@@ -3,32 +3,6 @@
3
3
  require 'tapyrus'
4
4
  require 'terminal-table'
5
5
 
6
- class EmptyTxChecker
7
- def check_sig(script_sig, pubkey, script_code)
8
- warn "Signature verification failed. You need to enter tx and input index."
9
- exit
10
- end
11
- def verify_sig(sig, pubkey, digest, allow_hybrid: false)
12
- warn "Signature verification failed. You need to enter tx and input index."
13
- exit
14
- end
15
- end
16
-
17
- def run_step(interpreter, script, chunk, index, is_redeem_script)
18
- if chunk.pushdata?
19
- puts "PUSH #{chunk.pushed_data.bth}"
20
- else
21
- puts "APPLY #{Tapyrus::Opcodes.opcode_to_name(chunk.opcode)}"
22
- end
23
- result = interpreter.next_step(script, chunk, index, is_redeem_script)
24
- if result.is_a?(FalseClass)
25
- warn "script failed. Reason: #{interpreter.error}"
26
- exit
27
- end
28
- rows = interpreter.stack.map{|s|[s]}.reverse
29
- table = Terminal::Table.new(title: 'Current Stack', rows: rows )
30
- puts table
31
- end
32
6
 
33
7
  print "Enter scriptPubkey: "
34
8
  script_pubkey_hex = gets.chomp
@@ -52,14 +26,13 @@ end
52
26
 
53
27
  print "Enter tx: "
54
28
  tx_hex = gets.chomp
55
- if tx_hex.length == 0
56
- tx_checker = EmptyTxChecker.new
57
- else
29
+ tx = nil
30
+ input_index = nil
31
+ unless tx_hex.length == 0
58
32
  print "Enter index of the input: "
59
33
  input_index = gets.chomp
60
- tx_checker = Tapyrus::TxChecker.new
61
34
  begin
62
- tx_checker.tx = Tapyrus::Tx.parse_from_payload(tx_hex.htb)
35
+ tx = Tapyrus::Tx.parse_from_payload(tx_hex.htb)
63
36
  rescue StandardError
64
37
  warn "Invalid tx data."
65
38
  exit
@@ -68,64 +41,31 @@ else
68
41
  warn "Index of input missing."
69
42
  exit
70
43
  end
71
- tx_checker.input_index = input_index.to_i
72
- if (tx_checker.tx.in.size - 1) < tx_checker.input_index
73
- warn "Tx does not have #{tx_checker.input_index}-th input."
74
- exit
75
- end
44
+ input_index = input_index.to_i
76
45
  end
77
46
 
78
- interpreter = Tapyrus::ScriptInterpreter.new(checker: tx_checker)
79
-
80
- target_script = script_sig
81
- is_redeem = false
82
- interpreter.reset_params
83
- chunks = target_script.chunks.each
84
- chunk_index = 0
85
- chunks_size = target_script.chunks.length
86
- stack_copy = nil
47
+ begin
48
+ debugger = Tapyrus::ScriptDebugger.new(script_pubkey: script_pubkey, script_sig: script_sig, tx: tx, index: input_index)
49
+ rescue ArgumentError => e
50
+ warn e.message
51
+ exit
52
+ end
87
53
 
88
54
  puts "The Script is ready to be executed; you can step execution it by putting the Enter key."
89
55
  print "> "
90
56
  while cmd = gets.chomp
91
57
  if cmd.length == 0
92
- if chunks_size == chunk_index
93
- if target_script == script_sig
94
- stack_copy = interpreter.stack.dup
95
- target_script = script_pubkey
96
- interpreter.reset_params
97
- chunks = target_script.chunks.each
98
- chunk_index = 0
99
- chunks_size = target_script.chunks.length
100
- elsif target_script == script_pubkey
101
- if interpreter.stack.empty? || !interpreter.cast_to_bool(interpreter.stack.last.htb)
102
- warn "Script evaluated without error but finished with a false/empty top stack element"
103
- exit
104
- end
105
- if script_pubkey.p2sh?
106
- interpreter.stack = stack_copy
107
- redeem_script = Tapyrus::Script.parse_from_payload(interpreter.stack.pop.htb)
108
- puts "APPLY P2SH: #{redeem_script}"
109
- rows = interpreter.stack.map{|s|[s]}.reverse
110
- table = Terminal::Table.new(title: 'Current Stack', rows: rows )
111
- puts table
112
- target_script = redeem_script
113
- chunks = target_script.chunks.each
114
- chunk_index = 0
115
- chunks_size = target_script.chunks.length
116
- else
117
- puts "Execution finished."
118
- exit
119
- end
120
- else
121
- puts "Execution finished."
122
- exit
123
- end
58
+ result = debugger.step
59
+ if result.halt?
60
+ puts result.message if result.message
61
+ warn result.error
62
+ exit
63
+ elsif result.finished?
64
+ puts "Execution finished."
65
+ exit
66
+ else
67
+ result.print_result
124
68
  end
125
- run_step(interpreter, target_script, chunks.next, chunk_index, is_redeem)
126
- chunk_index += 1
127
- else
128
- puts "Put enter key to step execution or Ctrl+D to exit."
129
69
  end
130
70
  print "> "
131
71
  end
@@ -10,6 +10,7 @@ module Tapyrus
10
10
 
11
11
  INVALID_PRIV_KEY = 'Private key is not in range [1..n-1].'
12
12
  INVALID_CHECKSUM = 'Invalid checksum.'
13
+ INVALID_PRIV_LENGTH = 'Private key must be 32 bytes.'
13
14
  end
14
15
  end
15
16
  end
@@ -25,7 +25,8 @@ module Tapyrus
25
25
  l = Tapyrus.hmac_sha512('Tapyrus seed', seed.htb)
26
26
  left = l[0..31].bth.to_i(16)
27
27
  raise 'invalid key' if left >= CURVE_ORDER || left == 0
28
- ext_key.key = Tapyrus::Key.new(priv_key: l[0..31].bth, key_type: Tapyrus::Key::TYPES[:compressed])
28
+ l_priv = ECDSA::Format::IntegerOctetString.encode(left, 32)
29
+ ext_key.key = Tapyrus::Key.new(priv_key: l_priv.bth, key_type: Tapyrus::Key::TYPES[:compressed])
29
30
  ext_key.chain_code = l[32..-1]
30
31
  ext_key
31
32
  end
@@ -107,7 +108,8 @@ module Tapyrus
107
108
  raise 'invalid key' if left >= CURVE_ORDER
108
109
  child_priv = (left + key.priv_key.to_i(16)) % CURVE_ORDER
109
110
  raise 'invalid key ' if child_priv >= CURVE_ORDER
110
- new_key.key = Tapyrus::Key.new(priv_key: child_priv.to_even_length_hex.rjust(64, '0'), key_type: key_type)
111
+ child_priv = ECDSA::Format::IntegerOctetString.encode(child_priv, 32)
112
+ new_key.key = Tapyrus::Key.new(priv_key: child_priv.bth, key_type: key_type)
111
113
  new_key.chain_code = l[32..-1]
112
114
  new_key.ver = version
113
115
  new_key
@@ -275,7 +277,8 @@ module Tapyrus
275
277
  l = Tapyrus.hmac_sha512(chain_code, data)
276
278
  left = l[0..31].bth.to_i(16)
277
279
  raise 'invalid key' if left >= CURVE_ORDER
278
- p1 = Tapyrus::Key.new(priv_key: left.to_s(16), key_type: Tapyrus::Key::TYPES[:uncompressed]).to_point
280
+ l_priv = ECDSA::Format::IntegerOctetString.encode(left, 32)
281
+ p1 = Tapyrus::Key.new(priv_key: l_priv.bth, key_type: Tapyrus::Key::TYPES[:uncompressed]).to_point
279
282
  p2 = Tapyrus::Key.new(pubkey: pubkey, key_type: key_type).to_point
280
283
  new_key.pubkey = (p1 + p2).to_hex
281
284
  new_key.chain_code = l[32..-1]
data/lib/tapyrus/key.rb CHANGED
@@ -41,7 +41,11 @@ module Tapyrus
41
41
  end
42
42
  @secp256k1_module = Tapyrus.secp_impl
43
43
  @priv_key = priv_key
44
- raise ArgumentError, Errors::Messages::INVALID_PRIV_KEY unless validate_private_key_range(@priv_key) if @priv_key
44
+
45
+ if @priv_key
46
+ raise ArgumentError, Errors::Messages::INVALID_PRIV_KEY unless validate_private_key_range(@priv_key)
47
+ raise ArgumentError, Errors::Messages::INVALID_PRIV_LENGTH unless @priv_key.htb.bytesize == 32
48
+ end
45
49
  if pubkey
46
50
  @pubkey = pubkey
47
51
  else
@@ -0,0 +1,164 @@
1
+ module Tapyrus
2
+ class ScriptDebugger
3
+ attr_reader :script_pubkey
4
+ attr_reader :script_sig
5
+ attr_reader :tx_checker
6
+ attr_reader :interpreter
7
+
8
+ attr_accessor :target_script
9
+ attr_accessor :is_redeem
10
+ attr_accessor :chunk_index
11
+ attr_accessor :chunk_size
12
+ attr_accessor :chunks
13
+ attr_accessor :stack_copy
14
+
15
+ # @param [Tapyrus::Script] script_pubkey
16
+ # @param [Tapyrus::Script] script_sig
17
+ # @param [Tapyrus::Tx] tx (optional)
18
+ # @param [Integer] index (optional) input index
19
+ # @raise [ArgumentError]
20
+ def initialize(script_pubkey:, script_sig:, tx: nil, index: nil)
21
+ @script_pubkey = script_pubkey
22
+ @script_sig = script_sig
23
+ if tx
24
+ raise ArgumentError, 'index should be specified' if index.nil?
25
+ @tx_checker = Tapyrus::TxChecker.new(tx: tx, input_index: index)
26
+ if (tx_checker.tx.in.size - 1) < tx_checker.input_index
27
+ raise ArgumentError, "Tx does not have #{tx_checker.input_index}-th input."
28
+ end
29
+ else
30
+ @tx_checker = EmptyTxChecker.new
31
+ end
32
+ @interpreter = Tapyrus::ScriptInterpreter.new(checker: tx_checker)
33
+ @interpreter.reset_params
34
+ @chunk_index = 0
35
+ @target_script = script_sig
36
+ @chunks = target_script.chunks.each
37
+ @chunk_size = target_script.chunks.length
38
+ @stack_copy = nil
39
+ end
40
+
41
+ def step
42
+ if chunk_size == chunk_index
43
+ if target_script == script_sig
44
+ @stack_copy = interpreter.stack.dup
45
+ @target_script = script_pubkey
46
+ @interpreter.reset_params
47
+ @chunks = target_script.chunks.each
48
+ @chunk_index = 0
49
+ @chunk_size = target_script.chunks.length
50
+ elsif target_script == script_pubkey
51
+ if interpreter.stack.empty? || !interpreter.cast_to_bool(interpreter.stack.last.htb)
52
+ return(
53
+ StepResult.error(
54
+ current_stack: interpreter.stack.dup,
55
+ error: 'Script evaluated without error but finished with a false/empty top stack element'
56
+ )
57
+ )
58
+ end
59
+ if script_pubkey.p2sh?
60
+ interpreter.stack = stack_copy
61
+ redeem_script = Tapyrus::Script.parse_from_payload(interpreter.stack.pop.htb)
62
+ @target_script = redeem_script
63
+ @chunks = target_script.chunks.each
64
+ @chunk_index = 0
65
+ @chunk_size = target_script.chunks.length
66
+ else
67
+ return StepResult.finished(current_stack: interpreter.stack.dup)
68
+ end
69
+ else
70
+ return StepResult.finished(current_stack: interpreter.stack.dup)
71
+ end
72
+ end
73
+ result = step_process(interpreter, target_script, chunks.next, chunk_index, is_redeem)
74
+ return result if result.is_a?(StepResult)
75
+ @chunk_index += 1
76
+ StepResult.success(current_stack: interpreter.stack.dup, message: result)
77
+ end
78
+
79
+ private
80
+
81
+ def step_process(interpreter, script, chunk, index, is_redeem_script)
82
+ message =
83
+ chunk.pushdata? ? "PUSH #{chunk.pushed_data.bth}" : "APPLY #{Tapyrus::Opcodes.opcode_to_name(chunk.opcode)}"
84
+ begin
85
+ result = interpreter.next_step(script, chunk, index, is_redeem_script)
86
+ if result.is_a?(FalseClass)
87
+ return(
88
+ StepResult.error(current_stack: interpreter.stack.dup, error: "script failed. Reason: #{interpreter.error}")
89
+ )
90
+ end
91
+ message
92
+ rescue TxUnspecifiedError => e
93
+ return StepResult.error(current_stack: interpreter.stack.dup, error: e.message, message: message)
94
+ end
95
+ end
96
+ end
97
+
98
+ class StepResult
99
+ STATUS_RUNNING = 'running'
100
+ STATUS_HALT = 'halt'
101
+ STATUS_FINISHED = 'finished'
102
+
103
+ STATUSES = [STATUS_RUNNING, STATUS_HALT, STATUS_FINISHED]
104
+
105
+ attr_reader :status
106
+ attr_reader :current_stack
107
+ attr_reader :message
108
+ attr_reader :error
109
+
110
+ # @param [String] status
111
+ # @param [Array] current_stack
112
+ # @param [String] message
113
+ # @param [String] error an error message.
114
+ def initialize(status, current_stack: [], message: nil, error: nil)
115
+ raise ArgumentError, 'Unsupported status specified.' unless STATUSES.include?(status)
116
+ @status = status
117
+ @current_stack = current_stack
118
+ @error = error
119
+ @message = message
120
+ end
121
+
122
+ def self.error(current_stack: [], error:, message: nil)
123
+ StepResult.new(STATUS_HALT, current_stack: current_stack, error: error, message: message)
124
+ end
125
+
126
+ def self.success(current_stack: [], message: nil)
127
+ StepResult.new(STATUS_RUNNING, current_stack: current_stack, message: message)
128
+ end
129
+
130
+ def self.finished(current_stack: [])
131
+ StepResult.new(STATUS_FINISHED, current_stack: current_stack)
132
+ end
133
+
134
+ def halt?
135
+ status == STATUS_HALT
136
+ end
137
+
138
+ def finished?
139
+ status == STATUS_FINISHED
140
+ end
141
+
142
+ def stack_table
143
+ rows = current_stack.map { |s| [s] }.reverse
144
+ Terminal::Table.new(title: 'Current Stack', rows: rows)
145
+ end
146
+
147
+ def print_result
148
+ puts message if message
149
+ puts stack_table
150
+ end
151
+ end
152
+
153
+ class TxUnspecifiedError < StandardError
154
+ end
155
+
156
+ class EmptyTxChecker
157
+ def check_sig(script_sig, pubkey, script_code)
158
+ raise TxUnspecifiedError, 'Signature verification failed. You need to enter tx and input index.'
159
+ end
160
+ def verify_sig(sig, pubkey, digest, allow_hybrid: false)
161
+ raise TxUnspecifiedError, 'Signature verification failed. You need to enter tx and input index.'
162
+ end
163
+ end
164
+ end
@@ -67,9 +67,6 @@ module Tapyrus
67
67
  def check_sequence(sequence)
68
68
  tx_sequence = tx.inputs[input_index].sequence
69
69
 
70
- # Fail if the transaction's version number is not set high enough to trigger BIP 68 rules.
71
- return false if tx.features < 2
72
-
73
70
  # Sequence numbers with their most significant bit set are not consensus constrained.
74
71
  # Testing that the transaction's sequence number do not have this bit set prevents using this property to get around a CHECKSEQUENCEVERIFY check.
75
72
  return false unless tx_sequence & TxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG == 0
@@ -1,3 +1,3 @@
1
1
  module Tapyrus
2
- VERSION = '0.3.2'
2
+ VERSION = '0.3.4'
3
3
  end
data/lib/tapyrus.rb CHANGED
@@ -24,6 +24,7 @@ module Tapyrus
24
24
  autoload :Script, 'tapyrus/script/script'
25
25
  autoload :Multisig, 'tapyrus/script/multisig'
26
26
  autoload :ScriptInterpreter, 'tapyrus/script/script_interpreter'
27
+ autoload :ScriptDebugger, 'tapyrus/script/debugger'
27
28
  autoload :ScriptError, 'tapyrus/script/script_error'
28
29
  autoload :TxChecker, 'tapyrus/script/tx_checker'
29
30
  autoload :TxIn, 'tapyrus/tx_in'
@@ -50,6 +51,7 @@ module Tapyrus
50
51
  autoload :Errors, 'tapyrus/errors'
51
52
  autoload :TxBuilder, 'tapyrus/tx_builder'
52
53
  autoload :BIP175, 'tapyrus/bip175'
54
+ autoload :Contract, 'tapyrus/contract'
53
55
 
54
56
  require_relative 'tapyrus/constants'
55
57
  require_relative 'tapyrus/ext/ecdsa'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tapyrus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-06 00:00:00.000000000 Z
11
+ date: 2023-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ecdsa
@@ -405,6 +405,7 @@ files:
405
405
  - lib/tapyrus/rpc/request_handler.rb
406
406
  - lib/tapyrus/rpc/tapyrus_core_client.rb
407
407
  - lib/tapyrus/script/color.rb
408
+ - lib/tapyrus/script/debugger.rb
408
409
  - lib/tapyrus/script/multisig.rb
409
410
  - lib/tapyrus/script/script.rb
410
411
  - lib/tapyrus/script/script_error.rb
@@ -456,7 +457,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
456
457
  - !ruby/object:Gem::Version
457
458
  version: '0'
458
459
  requirements: []
459
- rubygems_version: 3.3.21
460
+ rubygems_version: 3.3.23
460
461
  signing_key:
461
462
  specification_version: 4
462
463
  summary: The implementation of Tapyrus Protocol for Ruby.