tapyrus 0.3.2 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
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.