tapyrus 0.3.1 → 0.3.3

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: dc2e90197905239fab7d891f05da735cf1fb2df0127abbb1ca38c1299130914d
4
- data.tar.gz: 36a9a028b360a238077bd30c2380b3c722983d5dbd7e5f5d9e6e1d2dbd1408c0
3
+ metadata.gz: fe8d938cb082800dbacfbdbebc6ab00d14a40693a92a4df46f967d0535f9e337
4
+ data.tar.gz: b6a51b4a565435d2a5a4f65d6fbcec3420464d6067742c64d00651919de22d6d
5
5
  SHA512:
6
- metadata.gz: e336315f9e68a361edf4b0e2dec290a6e37f666375badc13336391289b773084d57ac324fa5e1b3334faa6400d931abd24dda05345901767a29e9f11251200bc
7
- data.tar.gz: a1a3df8abf276b794eed62cd7b238d57fa909315f116d5f12c4b472e32b24c8da1ff8cc62e656fb985cf5728e24a7821580b1f24f09613535a2be4b23f3bf1fa
6
+ metadata.gz: c2ead3ceffe67d128f94af033ff788fbb15a283416af5ed2afe915ad8af6dc34e36e6e5ad7e21fa94ea2137e8cde1441a3b15228402b46243b582e21ab0dea1d
7
+ data.tar.gz: 1dbefaca1c96fa263ba870f8f892f33a1161c8d7ba0e2ecedad1aea3af98112cb8836e03f85201a1c2d0febe010b49f73e4497195eca90e157081d4a049f265e
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Tapyrusrb [![Build Status](https://travis-ci.org/chaintope/tapyrusrb.svg?branch=master)](https://travis-ci.org/chaintope/tapyrusrb) [![Gem Version](https://badge.fury.io/rb/tapyrus.svg)](https://badge.fury.io/rb/tapyrus) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
1
+ # Tapyrusrb [![Build Status](https://github.com/chaintope/tapyrusrb/actions/workflows/ruby.yml/badge.svg?branch=master)](https://github.com/chaintope/tapyrusrb/actions/workflows/ruby.yml) [![Gem Version](https://badge.fury.io/rb/tapyrus.svg)](https://badge.fury.io/rb/tapyrus) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
2
2
 
3
3
  Tapyrusrb is a Ruby implementation of [Tapyrus](https://github.com/chaintope/tapyrus-core) Protocol.
4
4
 
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tapyrus'
4
+ require 'terminal-table'
5
+
6
+
7
+ print "Enter scriptPubkey: "
8
+ script_pubkey_hex = gets.chomp
9
+ script_pubkey = Tapyrus::Script.parse_from_payload(script_pubkey_hex.htb)
10
+ puts script_pubkey unless script_pubkey_hex.empty?
11
+
12
+ print "Enter scriptSig: "
13
+ script_sig_hex = gets.chomp
14
+ script_sig = Tapyrus::Script.parse_from_payload(script_sig_hex.htb)
15
+ puts script_sig unless script_sig_hex.empty?
16
+
17
+ unless script_sig.push_only?
18
+ warn "scriptSig has non-push opcode."
19
+ puts
20
+ end
21
+
22
+ if script_pubkey_hex.empty? && script_sig.empty?
23
+ puts "Empty script."
24
+ exit
25
+ end
26
+
27
+ print "Enter tx: "
28
+ tx_hex = gets.chomp
29
+ tx = nil
30
+ input_index = nil
31
+ unless tx_hex.length == 0
32
+ print "Enter index of the input: "
33
+ input_index = gets.chomp
34
+ begin
35
+ tx = Tapyrus::Tx.parse_from_payload(tx_hex.htb)
36
+ rescue StandardError
37
+ warn "Invalid tx data."
38
+ exit
39
+ end
40
+ if input_index.empty?
41
+ warn "Index of input missing."
42
+ exit
43
+ end
44
+ input_index = input_index.to_i
45
+ end
46
+
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
53
+
54
+ puts "The Script is ready to be executed; you can step execution it by putting the Enter key."
55
+ print "> "
56
+ while cmd = gets.chomp
57
+ if cmd.length == 0
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
68
+ end
69
+ end
70
+ print "> "
71
+ end
@@ -104,7 +104,7 @@ module Tapyrus
104
104
  end
105
105
  end
106
106
 
107
- # Check this header contains upgrade aggregated publiec key.
107
+ # Check this header contains upgrade aggregated public key.
108
108
  # @return [Boolean] if contains return true, otherwise false.
109
109
  def upgrade_agg_pubkey?
110
110
  x_field_type == X_FILED_TYPES[:aggregate_pubkey]
@@ -94,8 +94,6 @@ module Tapyrus
94
94
  # 80 bytes of data, +1 for OP_RETURN, +2 for the pushdata opcodes.
95
95
  MAX_OP_RETURN_RELAY = 83
96
96
 
97
- SIG_VERSION = [:base]
98
-
99
97
  # for script error
100
98
  SCRIPT_ERR_OK = 0
101
99
  SCRIPT_ERR_UNKNOWN_ERROR = 1
@@ -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