btcruby 1.1.3 → 1.1.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
  SHA1:
3
- metadata.gz: 96ac30298d4c5f04b49b6874d4a0f2f1e3548f4c
4
- data.tar.gz: 31c8e3bbb67643b86c70336d30638a1c1172b5d1
3
+ metadata.gz: 496c31ea05b95ca3fe1971a17eea51f43a2c00b6
4
+ data.tar.gz: 6a8cbbe43cd39b004f188f32c90e6c492d7f78d2
5
5
  SHA512:
6
- metadata.gz: 23d001c7a98c869e3c082181f5ed06321ff95dc1ae3e03647d6df60dc0abd58cfdaeb9ca16ac3dd65363c17339de52ccdb671461fc87fb130a55c4da5947a7d8
7
- data.tar.gz: d89027d38e01c7414888d5c30bf55932b54f58ab1772f0211276d7be1816ed1374cbedde5c25fd9a94904dc74dc97d04d50177922053977136f5dae9097aa773
6
+ metadata.gz: 5de016754bc4fdec389d62e4fbce3b3f104a00eb698fb1647c02c7531223cfb41066e3c13eb0a286cb91ba59950493af8edd9c061f427c0317cc424b699b3e43
7
+ data.tar.gz: 4514a56b1c78db5e1e3beaf3fc5983b964451bf4a007adeb359d2c16b05e0e4b291ecdf06917b689446f33ec22ef5bf9a36cfd6496d2bc37a955896a01606e23
@@ -2,6 +2,14 @@
2
2
  BTCRuby Release Notes
3
3
  =====================
4
4
 
5
+ 1.1.4 (August 18, 2015)
6
+ -----------------------
7
+
8
+ * Public API for `ScriptChunk` instances.
9
+ * Added `ScriptChunk#data_only?`.
10
+ * Added `ScriptChunk#interpreted_data`.
11
+
12
+
5
13
  1.1.3 (August 17, 2015)
6
14
  -----------------------
7
15
 
@@ -32,6 +32,7 @@ require_relative 'btcruby/validation.rb'
32
32
  require_relative 'btcruby/script/script_error.rb'
33
33
  require_relative 'btcruby/script/script_flags.rb'
34
34
  require_relative 'btcruby/script/script_number.rb'
35
+ require_relative 'btcruby/script/script_chunk.rb'
35
36
  require_relative 'btcruby/script/script.rb'
36
37
  require_relative 'btcruby/script/script_interpreter.rb'
37
38
  require_relative 'btcruby/script/opcode.rb'
@@ -38,161 +38,168 @@ module BTC
38
38
  return nil
39
39
  end
40
40
  end
41
-
42
- # 1. Operators pushing data on stack.
43
-
44
- # Push 1 byte 0x00 on the stack
45
- OP_FALSE = 0x00
46
- OP_0 = 0x00
47
-
48
- # Any opcode with value < PUSHDATA1 is a length of the string to be pushed on the stack.
49
- # So opcode 0x01 is followed by 1 byte of data, 0x09 by 9 bytes and so on up to 0x4b (75 bytes)
50
-
51
- # PUSHDATA<N> opcode is followed by N-byte length of the string that follows.
52
- OP_PUSHDATA1 = 0x4c # followed by a 1-byte length of the string to push (allows pushing 0..255 bytes).
53
- OP_PUSHDATA2 = 0x4d # followed by a 2-byte length of the string to push (allows pushing 0..65535 bytes).
54
- OP_PUSHDATA4 = 0x4e # followed by a 4-byte length of the string to push (allows pushing 0..4294967295 bytes).
55
- OP_1NEGATE = 0x4f # pushes -1 number on the stack
56
- OP_RESERVED = 0x50 # Not assigned. If executed, transaction is invalid.
57
-
58
- # OP_<N> pushes number <N> on the stack
59
- OP_TRUE = 0x51
60
- OP_1 = 0x51
61
- OP_2 = 0x52
62
- OP_3 = 0x53
63
- OP_4 = 0x54
64
- OP_5 = 0x55
65
- OP_6 = 0x56
66
- OP_7 = 0x57
67
- OP_8 = 0x58
68
- OP_9 = 0x59
69
- OP_10 = 0x5a
70
- OP_11 = 0x5b
71
- OP_12 = 0x5c
72
- OP_13 = 0x5d
73
- OP_14 = 0x5e
74
- OP_15 = 0x5f
75
- OP_16 = 0x60
76
-
77
- # 2. Control flow operators
78
-
79
- OP_NOP = 0x61 # Does nothing
80
- OP_VER = 0x62 # Not assigned. If executed, transaction is invalid.
81
-
82
- # BitcoinQT executes all operators from OP_IF to OP_ENDIF even inside "non-executed" branch (to keep track of nesting).
83
- # Since OP_VERIF and OP_VERNOTIF are not assigned, even inside a non-executed branch they will fall in "default:" switch case
84
- # and cause the script to fail. Some other ops like OP_VER can be present inside non-executed branch because they'll be skipped.
85
- OP_IF = 0x63 # If the top stack value is not 0, the statements are executed. The top stack value is removed.
86
- OP_NOTIF = 0x64 # If the top stack value is 0, the statements are executed. The top stack value is removed.
87
- OP_VERIF = 0x65 # Not assigned. Script is invalid with that opcode (even if inside non-executed branch).
88
- OP_VERNOTIF = 0x66 # Not assigned. Script is invalid with that opcode (even if inside non-executed branch).
89
- OP_ELSE = 0x67 # Executes code if the previous OP_IF or OP_NOTIF was not executed.
90
- OP_ENDIF = 0x68 # Finishes if/else block
91
-
92
- OP_VERIFY = 0x69 # Removes item from the stack if it's not 0x00 or 0x80 (negative zero). Otherwise, marks script as invalid.
93
- OP_RETURN = 0x6a # Marks transaction as invalid.
94
-
95
- # Stack ops
96
- OP_TOALTSTACK = 0x6b # Moves item from the stack to altstack
97
- OP_FROMALTSTACK = 0x6c # Moves item from the altstack to stack
98
- OP_2DROP = 0x6d
99
- OP_2DUP = 0x6e
100
- OP_3DUP = 0x6f
101
- OP_2OVER = 0x70
102
- OP_2ROT = 0x71
103
- OP_2SWAP = 0x72
104
- OP_IFDUP = 0x73
105
- OP_DEPTH = 0x74
106
- OP_DROP = 0x75
107
- OP_DUP = 0x76
108
- OP_NIP = 0x77
109
- OP_OVER = 0x78
110
- OP_PICK = 0x79
111
- OP_ROLL = 0x7a
112
- OP_ROT = 0x7b
113
- OP_SWAP = 0x7c
114
- OP_TUCK = 0x7d
115
-
116
- # Splice ops
117
- OP_CAT = 0x7e # Disabled opcode. If executed, transaction is invalid.
118
- OP_SUBSTR = 0x7f # Disabled opcode. If executed, transaction is invalid.
119
- OP_LEFT = 0x80 # Disabled opcode. If executed, transaction is invalid.
120
- OP_RIGHT = 0x81 # Disabled opcode. If executed, transaction is invalid.
121
- OP_SIZE = 0x82
122
-
123
- # Bit logic
124
- OP_INVERT = 0x83 # Disabled opcode. If executed, transaction is invalid.
125
- OP_AND = 0x84 # Disabled opcode. If executed, transaction is invalid.
126
- OP_OR = 0x85 # Disabled opcode. If executed, transaction is invalid.
127
- OP_XOR = 0x86 # Disabled opcode. If executed, transaction is invalid.
128
-
129
- OP_EQUAL = 0x87 # Last two items are removed from the stack and compared. Result (true or false) is pushed to the stack.
130
- OP_EQUALVERIFY = 0x88 # Same as OP_EQUAL, but removes the result from the stack if it's true or marks script as invalid.
131
-
132
- OP_RESERVED1 = 0x89 # Disabled opcode. If executed, transaction is invalid.
133
- OP_RESERVED2 = 0x8a # Disabled opcode. If executed, transaction is invalid.
134
-
135
- # Numeric
136
- OP_1ADD = 0x8b # adds 1 to last item, pops it from stack and pushes result.
137
- OP_1SUB = 0x8c # substracts 1 to last item, pops it from stack and pushes result.
138
- OP_2MUL = 0x8d # Disabled opcode. If executed, transaction is invalid.
139
- OP_2DIV = 0x8e # Disabled opcode. If executed, transaction is invalid.
140
- OP_NEGATE = 0x8f # negates the number, pops it from stack and pushes result.
141
- OP_ABS = 0x90 # replaces number with its absolute value
142
- OP_NOT = 0x91 # replaces number with True if it's zero, False otherwise.
143
- OP_0NOTEQUAL = 0x92 # replaces number with True if it's not zero, False otherwise.
144
-
145
- OP_ADD = 0x93 # (x y -- x+y)
146
- OP_SUB = 0x94 # (x y -- x-y)
147
- OP_MUL = 0x95 # Disabled opcode. If executed, transaction is invalid.
148
- OP_DIV = 0x96 # Disabled opcode. If executed, transaction is invalid.
149
- OP_MOD = 0x97 # Disabled opcode. If executed, transaction is invalid.
150
- OP_LSHIFT = 0x98 # Disabled opcode. If executed, transaction is invalid.
151
- OP_RSHIFT = 0x99 # Disabled opcode. If executed, transaction is invalid.
152
-
153
- OP_BOOLAND = 0x9a
154
- OP_BOOLOR = 0x9b
155
- OP_NUMEQUAL = 0x9c
156
- OP_NUMEQUALVERIFY = 0x9d
157
- OP_NUMNOTEQUAL = 0x9e
158
- OP_LESSTHAN = 0x9f
159
- OP_GREATERTHAN = 0xa0
160
- OP_LESSTHANOREQUAL = 0xa1
161
- OP_GREATERTHANOREQUAL = 0xa2
162
- OP_MIN = 0xa3
163
- OP_MAX = 0xa4
164
-
165
- OP_WITHIN = 0xa5
166
-
167
- # Crypto
168
- OP_RIPEMD160 = 0xa6
169
- OP_SHA1 = 0xa7
170
- OP_SHA256 = 0xa8
171
- OP_HASH160 = 0xa9
172
- OP_HASH256 = 0xaa
173
- OP_CODESEPARATOR = 0xab # This opcode is rarely used because it's useless, but we need to support it anyway.
174
- OP_CHECKSIG = 0xac
175
- OP_CHECKSIGVERIFY = 0xad
176
- OP_CHECKMULTISIG = 0xae
177
- OP_CHECKMULTISIGVERIFY = 0xaf
178
-
179
- # Expansion
180
- OP_NOP1 = 0xb0
181
- OP_NOP2 = 0xb1
182
- OP_CHECKLOCKTIMEVERIFY = OP_NOP2 # See BIP65
183
- OP_NOP3 = 0xb2
184
- OP_NOP4 = 0xb3
185
- OP_NOP5 = 0xb4
186
- OP_NOP6 = 0xb5
187
- OP_NOP7 = 0xb6
188
- OP_NOP8 = 0xb7
189
- OP_NOP9 = 0xb8
190
- OP_NOP10 = 0xb9
191
-
192
- OP_INVALIDOPCODE = 0xff
193
-
194
- OPCODE_NAME_TO_VALUE = Hash[*constants.grep(/^OP_/).map{|c| [c.to_s, const_get(c)] }.flatten]
195
- OPCODE_VALUE_TO_NAME = Hash[*constants.grep(/^OP_/).map{|c| [const_get(c), c.to_s] }.flatten]
196
-
41
+
42
+ # All opcodes are defined in Opcodes module so they can be included in
43
+ # a different namespace without the baggage of other BTC identifiers.
44
+ module Opcodes
45
+
46
+ # 1. Operators pushing data on stack.
47
+
48
+ # Push 1 byte 0x00 on the stack
49
+ OP_FALSE = 0x00
50
+ OP_0 = 0x00
51
+
52
+ # Any opcode with value < PUSHDATA1 is a length of the string to be pushed on the stack.
53
+ # So opcode 0x01 is followed by 1 byte of data, 0x09 by 9 bytes and so on up to 0x4b (75 bytes)
54
+
55
+ # PUSHDATA<N> opcode is followed by N-byte length of the string that follows.
56
+ OP_PUSHDATA1 = 0x4c # followed by a 1-byte length of the string to push (allows pushing 0..255 bytes).
57
+ OP_PUSHDATA2 = 0x4d # followed by a 2-byte length of the string to push (allows pushing 0..65535 bytes).
58
+ OP_PUSHDATA4 = 0x4e # followed by a 4-byte length of the string to push (allows pushing 0..4294967295 bytes).
59
+ OP_1NEGATE = 0x4f # pushes -1 number on the stack
60
+ OP_RESERVED = 0x50 # Not assigned. If executed, transaction is invalid.
61
+
62
+ # OP_<N> pushes number <N> on the stack
63
+ OP_TRUE = 0x51
64
+ OP_1 = 0x51
65
+ OP_2 = 0x52
66
+ OP_3 = 0x53
67
+ OP_4 = 0x54
68
+ OP_5 = 0x55
69
+ OP_6 = 0x56
70
+ OP_7 = 0x57
71
+ OP_8 = 0x58
72
+ OP_9 = 0x59
73
+ OP_10 = 0x5a
74
+ OP_11 = 0x5b
75
+ OP_12 = 0x5c
76
+ OP_13 = 0x5d
77
+ OP_14 = 0x5e
78
+ OP_15 = 0x5f
79
+ OP_16 = 0x60
80
+
81
+ # 2. Control flow operators
82
+
83
+ OP_NOP = 0x61 # Does nothing
84
+ OP_VER = 0x62 # Not assigned. If executed, transaction is invalid.
85
+
86
+ # BitcoinQT executes all operators from OP_IF to OP_ENDIF even inside "non-executed" branch (to keep track of nesting).
87
+ # Since OP_VERIF and OP_VERNOTIF are not assigned, even inside a non-executed branch they will fall in "default:" switch case
88
+ # and cause the script to fail. Some other ops like OP_VER can be present inside non-executed branch because they'll be skipped.
89
+ OP_IF = 0x63 # If the top stack value is not 0, the statements are executed. The top stack value is removed.
90
+ OP_NOTIF = 0x64 # If the top stack value is 0, the statements are executed. The top stack value is removed.
91
+ OP_VERIF = 0x65 # Not assigned. Script is invalid with that opcode (even if inside non-executed branch).
92
+ OP_VERNOTIF = 0x66 # Not assigned. Script is invalid with that opcode (even if inside non-executed branch).
93
+ OP_ELSE = 0x67 # Executes code if the previous OP_IF or OP_NOTIF was not executed.
94
+ OP_ENDIF = 0x68 # Finishes if/else block
95
+
96
+ OP_VERIFY = 0x69 # Removes item from the stack if it's not 0x00 or 0x80 (negative zero). Otherwise, marks script as invalid.
97
+ OP_RETURN = 0x6a # Marks transaction as invalid.
98
+
99
+ # Stack ops
100
+ OP_TOALTSTACK = 0x6b # Moves item from the stack to altstack
101
+ OP_FROMALTSTACK = 0x6c # Moves item from the altstack to stack
102
+ OP_2DROP = 0x6d
103
+ OP_2DUP = 0x6e
104
+ OP_3DUP = 0x6f
105
+ OP_2OVER = 0x70
106
+ OP_2ROT = 0x71
107
+ OP_2SWAP = 0x72
108
+ OP_IFDUP = 0x73
109
+ OP_DEPTH = 0x74
110
+ OP_DROP = 0x75
111
+ OP_DUP = 0x76
112
+ OP_NIP = 0x77
113
+ OP_OVER = 0x78
114
+ OP_PICK = 0x79
115
+ OP_ROLL = 0x7a
116
+ OP_ROT = 0x7b
117
+ OP_SWAP = 0x7c
118
+ OP_TUCK = 0x7d
119
+
120
+ # Splice ops
121
+ OP_CAT = 0x7e # Disabled opcode. If executed, transaction is invalid.
122
+ OP_SUBSTR = 0x7f # Disabled opcode. If executed, transaction is invalid.
123
+ OP_LEFT = 0x80 # Disabled opcode. If executed, transaction is invalid.
124
+ OP_RIGHT = 0x81 # Disabled opcode. If executed, transaction is invalid.
125
+ OP_SIZE = 0x82
126
+
127
+ # Bit logic
128
+ OP_INVERT = 0x83 # Disabled opcode. If executed, transaction is invalid.
129
+ OP_AND = 0x84 # Disabled opcode. If executed, transaction is invalid.
130
+ OP_OR = 0x85 # Disabled opcode. If executed, transaction is invalid.
131
+ OP_XOR = 0x86 # Disabled opcode. If executed, transaction is invalid.
132
+
133
+ OP_EQUAL = 0x87 # Last two items are removed from the stack and compared. Result (true or false) is pushed to the stack.
134
+ OP_EQUALVERIFY = 0x88 # Same as OP_EQUAL, but removes the result from the stack if it's true or marks script as invalid.
135
+
136
+ OP_RESERVED1 = 0x89 # Disabled opcode. If executed, transaction is invalid.
137
+ OP_RESERVED2 = 0x8a # Disabled opcode. If executed, transaction is invalid.
138
+
139
+ # Numeric
140
+ OP_1ADD = 0x8b # adds 1 to last item, pops it from stack and pushes result.
141
+ OP_1SUB = 0x8c # substracts 1 to last item, pops it from stack and pushes result.
142
+ OP_2MUL = 0x8d # Disabled opcode. If executed, transaction is invalid.
143
+ OP_2DIV = 0x8e # Disabled opcode. If executed, transaction is invalid.
144
+ OP_NEGATE = 0x8f # negates the number, pops it from stack and pushes result.
145
+ OP_ABS = 0x90 # replaces number with its absolute value
146
+ OP_NOT = 0x91 # replaces number with True if it's zero, False otherwise.
147
+ OP_0NOTEQUAL = 0x92 # replaces number with True if it's not zero, False otherwise.
148
+
149
+ OP_ADD = 0x93 # (x y -- x+y)
150
+ OP_SUB = 0x94 # (x y -- x-y)
151
+ OP_MUL = 0x95 # Disabled opcode. If executed, transaction is invalid.
152
+ OP_DIV = 0x96 # Disabled opcode. If executed, transaction is invalid.
153
+ OP_MOD = 0x97 # Disabled opcode. If executed, transaction is invalid.
154
+ OP_LSHIFT = 0x98 # Disabled opcode. If executed, transaction is invalid.
155
+ OP_RSHIFT = 0x99 # Disabled opcode. If executed, transaction is invalid.
156
+
157
+ OP_BOOLAND = 0x9a
158
+ OP_BOOLOR = 0x9b
159
+ OP_NUMEQUAL = 0x9c
160
+ OP_NUMEQUALVERIFY = 0x9d
161
+ OP_NUMNOTEQUAL = 0x9e
162
+ OP_LESSTHAN = 0x9f
163
+ OP_GREATERTHAN = 0xa0
164
+ OP_LESSTHANOREQUAL = 0xa1
165
+ OP_GREATERTHANOREQUAL = 0xa2
166
+ OP_MIN = 0xa3
167
+ OP_MAX = 0xa4
168
+
169
+ OP_WITHIN = 0xa5
170
+
171
+ # Crypto
172
+ OP_RIPEMD160 = 0xa6
173
+ OP_SHA1 = 0xa7
174
+ OP_SHA256 = 0xa8
175
+ OP_HASH160 = 0xa9
176
+ OP_HASH256 = 0xaa
177
+ OP_CODESEPARATOR = 0xab # This opcode is rarely used because it's useless, but we need to support it anyway.
178
+ OP_CHECKSIG = 0xac
179
+ OP_CHECKSIGVERIFY = 0xad
180
+ OP_CHECKMULTISIG = 0xae
181
+ OP_CHECKMULTISIGVERIFY = 0xaf
182
+
183
+ # Expansion
184
+ OP_NOP1 = 0xb0
185
+ OP_NOP2 = 0xb1
186
+ OP_CHECKLOCKTIMEVERIFY = OP_NOP2 # See BIP65
187
+ OP_NOP3 = 0xb2
188
+ OP_NOP4 = 0xb3
189
+ OP_NOP5 = 0xb4
190
+ OP_NOP6 = 0xb5
191
+ OP_NOP7 = 0xb6
192
+ OP_NOP8 = 0xb7
193
+ OP_NOP9 = 0xb8
194
+ OP_NOP10 = 0xb9
195
+
196
+ OP_INVALIDOPCODE = 0xff
197
+
198
+ OPCODE_NAME_TO_VALUE = Hash[*constants.grep(/^OP_/).map{|c| [c.to_s, const_get(c)] }.flatten]
199
+ OPCODE_VALUE_TO_NAME = Hash[*constants.grep(/^OP_/).map{|c| [const_get(c), c.to_s] }.flatten]
200
+ end
201
+
202
+ # Make all opcodes available as BTC::OP_...
203
+ include Opcodes
197
204
  end
198
205
 
@@ -13,6 +13,9 @@ module BTC
13
13
  # If it is not a multisig script, returns nil.
14
14
  # See also #multisig_script?
15
15
  attr_reader :multisig_signatures_required
16
+
17
+ # Returns an array of chunks that constitute the script.
18
+ attr_reader :chunks
16
19
 
17
20
  def initialize(hex: nil, # raw script data in hex
18
21
  data: nil, # raw script data in binary
@@ -25,7 +28,7 @@ module BTC
25
28
  @chunks = []
26
29
  offset = 0
27
30
  while offset < data.bytesize
28
- chunk = Chunk.with_data(data, offset: offset)
31
+ chunk = ScriptChunk.with_data(data, offset: offset)
29
32
  if !chunk.canonical?
30
33
  Diagnostics.current.add_message("BTC::Script: decoded non-canonical chunk at offset #{offset}: #{chunk.to_s}")
31
34
  end
@@ -249,7 +252,7 @@ module BTC
249
252
  def data_only?
250
253
  # Include PUSHDATA ops and OP_0..OP_16 literals.
251
254
  @chunks.each do |chunk|
252
- return false if chunk.opcode > OP_16
255
+ return false if !chunk.data_only?
253
256
  end
254
257
  return true
255
258
  end
@@ -377,7 +380,7 @@ module BTC
377
380
  if opcode > 0 && opcode <= OP_PUSHDATA4
378
381
  raise ArgumentError, "Cannot add pushdata opcode without data"
379
382
  end
380
- @chunks << Chunk.new(opcode.chr)
383
+ @chunks << ScriptChunk.new(opcode.chr)
381
384
  return self
382
385
  end
383
386
 
@@ -390,7 +393,7 @@ module BTC
390
393
  if !encoded_pushdata
391
394
  raise ArgumentError, "Cannot encode pushdata with opcode #{opcode}"
392
395
  end
393
- @chunks << Chunk.new(encoded_pushdata)
396
+ @chunks << ScriptChunk.new(encoded_pushdata)
394
397
  return self
395
398
  end
396
399
 
@@ -434,7 +437,7 @@ module BTC
434
437
  object.each do |element|
435
438
  self << element
436
439
  end
437
- elsif object.is_a?(Chunk)
440
+ elsif object.is_a?(ScriptChunk)
438
441
  @chunks << object
439
442
  else
440
443
  raise ArgumentError, "Operand must be an integer, a string a BTC::Script instance or an array of any of those."
@@ -490,8 +493,6 @@ module BTC
490
493
  # Private API
491
494
  # -----------
492
495
 
493
- attr_reader :chunks
494
-
495
496
  # If opcode is nil, then the most compact encoding will be chosen.
496
497
  # Returns nil if opcode can't be used for data, or data is nil or too big.
497
498
  def self.encode_pushdata(pushdata, opcode: nil)
@@ -561,199 +562,5 @@ module BTC
561
562
  return true
562
563
  end
563
564
 
564
- # Script::Chunk represents either an opcode or a pushdata command.
565
- class Chunk
566
- # Raw data for this chunk.
567
- # 1 byte for regular opcode, 1 or more bytes for pushdata command.
568
- # We do not call it 'data' to avoid confusion with `pushdata` (see below).
569
- # The encoding is guaranteed to be binary.
570
- attr_reader :raw_data
571
-
572
- # Opcode for this chunk (first byte of the raw_data).
573
- attr_reader :opcode
574
-
575
- # If opcode is OP_PUSHDATA*, contains pure data being pushed.
576
- # If opcode is OP_0, returns an empty string.
577
- # If opcode is not pushdata, returns nil.
578
- attr_reader :pushdata
579
-
580
- # Length of raw_data in bytes.
581
- attr_reader :size
582
- alias :length :size
583
-
584
- # Returns true if this is a non-pushdata (also not OP_0) opcode.
585
- def opcode?
586
- !pushdata?
587
- end
588
-
589
- # Returns true if this is a pushdata chunk (or OP_0 opcode).
590
- def pushdata?
591
- # Compact pushdata opcodes are "virtual", just length prefixes.
592
- # Attention: OP_0 is also "pushdata" code that pushes empty data.
593
- self.opcode <= OP_PUSHDATA4
594
- end
595
-
596
- # Returns true if this chunk is in canonical form (the most compact one).
597
- # Returns false if it contains pushdata with too big length prefix.
598
- # Example of non-canonical chunk: 75 bytes pushed with OP_PUSHDATA1 instead
599
- # of simple 0x4b prefix.
600
- def canonical?
601
- opcode = self.opcode
602
- if opcode < OP_PUSHDATA1
603
- return true # most compact pushdata is always canonical.
604
- elsif opcode == OP_PUSHDATA1
605
- return (self.raw_data.bytesize - (1+1)) >= OP_PUSHDATA1
606
- elsif opcode == OP_PUSHDATA2
607
- return (self.raw_data.bytesize - (1+2)) > 0xff
608
- elsif opcode == OP_PUSHDATA4
609
- return (self.raw_data.bytesize - (1+4)) > 0xffff
610
- else
611
- return true # all other opcodes are canonical (just 1 byte code)
612
- end
613
- end
614
-
615
- def opcode
616
- # raises StopIteration if raw_data is empty,
617
- # but we don't allow empty raw_data for chunks.
618
- raw_data.each_byte.next
619
- end
620
-
621
- def pushdata
622
- return nil if !pushdata?
623
- opcode = self.opcode
624
- offset = 1 # by default, opcode is just a length prefix.
625
- if opcode == OP_PUSHDATA1
626
- offset += 1
627
- elsif opcode == OP_PUSHDATA2
628
- offset += 2
629
- elsif opcode == OP_PUSHDATA4
630
- offset += 4
631
- end
632
- self.raw_data[offset, self.raw_data.size - offset]
633
- end
634
-
635
- def size
636
- self.raw_data.bytesize
637
- end
638
-
639
- def to_s
640
- opcode = self.opcode
641
-
642
- if self.opcode?
643
- return "OP_0" if opcode == OP_0
644
- return "OP_1NEGATE" if opcode == OP_1NEGATE
645
- if opcode >= OP_1 && opcode <= OP_16
646
- return "OP_#{opcode + 1 - OP_1}"
647
- end
648
- return Opcode.name_for_opcode(opcode)
649
- end
650
-
651
- pushdata = self.pushdata
652
- return "OP_0" if pushdata.bytesize == 0 # Empty data is encoded as OP_0.
653
-
654
- string = ""
655
-
656
- # If it's some weird readable string, show it as a readable string.
657
- if data_is_ascii_printable?(pushdata) && (pushdata.bytesize < 20 || pushdata.bytesize > 65)
658
- string = pushdata.encode(Encoding::ASCII)
659
- # Escape escapes & single quote characters.
660
- string.gsub!("\\", "\\\\")
661
- string.gsub!("'", "\\'")
662
- # Wrap in single quotes. Why not double? Because they are already used in JSON and we don't want to multiply the mess.
663
- string = "'#{string}'"
664
- else
665
- string = BTC.to_hex(pushdata)
666
- # Shorter than 128-bit chunks are wrapped in square brackets to avoid ambiguity with big all-decimal numbers.
667
- if (pushdata.bytesize < 16)
668
- string = "[#{string}]"
669
- end
670
- end
671
-
672
- # Pushdata with non-compact encoding will have explicit length prefix (1 for OP_PUSHDATA1, 2 for OP_PUSHDATA2 and 4 for OP_PUSHDATA4).
673
- if !canonical?
674
- prefix = 1
675
- prefix = 2 if opcode == OP_PUSHDATA2
676
- prefix = 4 if opcode == OP_PUSHDATA4
677
- string = "#{prefix}:#{string}"
678
- end
679
-
680
- return string
681
- end
682
-
683
- # Parses the chunk with binary data. Assumes the encoding is binary.
684
- def self.with_data(data, offset: 0)
685
- raise ArgumentError, "Data is missing" if !data
686
-
687
- opcode, _ = BTC::WireFormat.read_uint8(data: data, offset: offset)
688
-
689
- raise ArgumentError, "Failed to read opcode of the script chunk" if !opcode
690
-
691
- # push data opcode
692
- if opcode <= OP_PUSHDATA4
693
-
694
- length = data.bytesize
695
-
696
- if opcode < OP_PUSHDATA1
697
- pushdata_length = opcode
698
- chunk_length = 1 + pushdata_length
699
- if offset + chunk_length > length
700
- raise ArgumentError, "PUSHDATA is longer than we have bytes available"
701
- end
702
- return self.new(data[offset, chunk_length])
703
- elsif opcode == OP_PUSHDATA1
704
- pushdata_length, _ = BTC::WireFormat.read_uint8(data: data, offset: offset + 1)
705
- if !pushdata_length
706
- raise ArgumentError, "Failed to read length for PUSHDATA1"
707
- end
708
- chunk_length = 1 + 1 + pushdata_length
709
- if offset + chunk_length > length
710
- raise ArgumentError, "PUSHDATA1 is longer than we have bytes available"
711
- end
712
- return self.new(data[offset, chunk_length])
713
- elsif (opcode == OP_PUSHDATA2)
714
- pushdata_length, _ = BTC::WireFormat.read_uint16le(data: data, offset: offset + 1)
715
- if !pushdata_length
716
- raise ArgumentError, "Failed to read length for PUSHDATA2"
717
- end
718
- chunk_length = 1 + 2 + pushdata_length
719
- if offset + chunk_length > length
720
- raise ArgumentError, "PUSHDATA2 is longer than we have bytes available"
721
- end
722
- return self.new(data[offset, chunk_length])
723
- elsif (opcode == OP_PUSHDATA4)
724
- pushdata_length, _ = BTC::WireFormat.read_uint32le(data: data, offset: offset + 1)
725
- if !pushdata_length
726
- raise ArgumentError, "Failed to read length for PUSHDATA4"
727
- end
728
- chunk_length = 1 + 4 + pushdata_length
729
- if offset + chunk_length > length
730
- raise ArgumentError, "PUSHDATA4 is longer than we have bytes available"
731
- end
732
- return self.new(data[offset, chunk_length])
733
- end
734
- else
735
- # simple opcode - 1 byte
736
- return self.new(data[offset, 1])
737
- end
738
- end
739
-
740
- def initialize(raw_data)
741
- @raw_data = raw_data
742
- end
743
-
744
- def ==(other)
745
- @raw_data == other.raw_data
746
- end
747
-
748
- protected
749
-
750
- def data_is_ascii_printable?(data)
751
- data.each_byte do |byte|
752
- return false if !(byte >= 0x20 && byte <= 0x7E)
753
- end
754
- return true
755
- end
756
-
757
- end # Chunk
758
565
  end # Script
759
566
  end # BTC
@@ -0,0 +1,216 @@
1
+ module BTC
2
+ # ScriptChunk represents either an opcode or a pushdata command.
3
+ class ScriptChunk
4
+ # Raw data for this chunk.
5
+ # 1 byte for regular opcode, 1 or more bytes for pushdata command.
6
+ # We do not call it 'data' to avoid confusion with `pushdata` (see below).
7
+ # The encoding is guaranteed to be binary.
8
+ attr_reader :raw_data
9
+
10
+ # Opcode for this chunk (first byte of the raw_data).
11
+ attr_reader :opcode
12
+
13
+ # If opcode is OP_PUSHDATA*, contains pure data being pushed.
14
+ # If opcode is OP_0, returns an empty string.
15
+ # If opcode is not pushdata, returns nil.
16
+ attr_reader :pushdata
17
+
18
+ # Length of raw_data in bytes.
19
+ attr_reader :size
20
+ alias :length :size
21
+
22
+ # Returns true if this is a non-pushdata (also not OP_0) opcode.
23
+ def opcode?
24
+ !pushdata?
25
+ end
26
+
27
+ # Returns true if this is a pushdata chunk (or OP_0 opcode).
28
+ def pushdata?
29
+ # Compact pushdata opcodes are "virtual", just length prefixes.
30
+ # Attention: OP_0 is also "pushdata" code that pushes empty data.
31
+ opcode <= OP_PUSHDATA4
32
+ end
33
+
34
+ def data_only?
35
+ opcode <= OP_16
36
+ end
37
+
38
+ # Returns true if this chunk is in canonical form (the most compact one).
39
+ # Returns false if it contains pushdata with too big length prefix.
40
+ # Example of non-canonical chunk: 75 bytes pushed with OP_PUSHDATA1 instead
41
+ # of simple 0x4b prefix.
42
+ # Note: this is not as strict as `check_minimal_push` in ScriptInterpreter.
43
+ def canonical?
44
+ opcode = self.opcode
45
+ if opcode < OP_PUSHDATA1
46
+ return true # most compact pushdata is always canonical.
47
+ elsif opcode == OP_PUSHDATA1
48
+ return (self.raw_data.bytesize - (1+1)) >= OP_PUSHDATA1
49
+ elsif opcode == OP_PUSHDATA2
50
+ return (self.raw_data.bytesize - (1+2)) > 0xff
51
+ elsif opcode == OP_PUSHDATA4
52
+ return (self.raw_data.bytesize - (1+4)) > 0xffff
53
+ else
54
+ return true # all other opcodes are canonical (just 1 byte code)
55
+ end
56
+ end
57
+
58
+ def opcode
59
+ # raises StopIteration if raw_data is empty,
60
+ # but we don't allow empty raw_data for chunks.
61
+ raw_data.each_byte.next
62
+ end
63
+
64
+ def pushdata
65
+ return nil if !pushdata?
66
+ opcode = self.opcode
67
+ offset = 1 # by default, opcode is just a length prefix.
68
+ if opcode == OP_PUSHDATA1
69
+ offset += 1
70
+ elsif opcode == OP_PUSHDATA2
71
+ offset += 2
72
+ elsif opcode == OP_PUSHDATA4
73
+ offset += 4
74
+ end
75
+ self.raw_data[offset, self.raw_data.size - offset]
76
+ end
77
+
78
+ # Returns corresponding data for data_only opcodes:
79
+ # pushdata or OP_N-encoded numbers.
80
+ # For all other opcodes returns nil.
81
+ def interpreted_data
82
+ if d = pushdata
83
+ return d
84
+ end
85
+ opcode = self.opcode
86
+ if opcode == OP_1NEGATE || (opcode >= OP_1 && opcode <= OP_16)
87
+ ScriptNumber.new(integer: opcode - OP_1 + 1).data
88
+ else
89
+ nil
90
+ end
91
+ end
92
+
93
+ def size
94
+ self.raw_data.bytesize
95
+ end
96
+
97
+ def to_s
98
+ opcode = self.opcode
99
+
100
+ if self.opcode?
101
+ return "OP_0" if opcode == OP_0
102
+ return "OP_1NEGATE" if opcode == OP_1NEGATE
103
+ if opcode >= OP_1 && opcode <= OP_16
104
+ return "OP_#{opcode + 1 - OP_1}"
105
+ end
106
+ return Opcode.name_for_opcode(opcode)
107
+ end
108
+
109
+ pushdata = self.pushdata
110
+ return "OP_0" if pushdata.bytesize == 0 # Empty data is encoded as OP_0.
111
+
112
+ string = ""
113
+
114
+ # If it's some weird readable string, show it as a readable string.
115
+ if data_is_ascii_printable?(pushdata) && (pushdata.bytesize < 20 || pushdata.bytesize > 65)
116
+ string = pushdata.encode(Encoding::ASCII)
117
+ # Escape escapes & single quote characters.
118
+ string.gsub!("\\", "\\\\")
119
+ string.gsub!("'", "\\'")
120
+ # Wrap in single quotes. Why not double? Because they are already used in JSON and we don't want to multiply the mess.
121
+ string = "'#{string}'"
122
+ else
123
+ string = BTC.to_hex(pushdata)
124
+ # Shorter than 128-bit chunks are wrapped in square brackets to avoid ambiguity with big all-decimal numbers.
125
+ if (pushdata.bytesize < 16)
126
+ string = "[#{string}]"
127
+ end
128
+ end
129
+
130
+ # Pushdata with non-compact encoding will have explicit length prefix (1 for OP_PUSHDATA1, 2 for OP_PUSHDATA2 and 4 for OP_PUSHDATA4).
131
+ if !canonical?
132
+ prefix = 1
133
+ prefix = 2 if opcode == OP_PUSHDATA2
134
+ prefix = 4 if opcode == OP_PUSHDATA4
135
+ string = "#{prefix}:#{string}"
136
+ end
137
+
138
+ return string
139
+ end
140
+
141
+ # Parses the chunk with binary data. Assumes the encoding is binary.
142
+ def self.with_data(data, offset: 0)
143
+ raise ArgumentError, "Data is missing" if !data
144
+
145
+ opcode, _ = BTC::WireFormat.read_uint8(data: data, offset: offset)
146
+
147
+ raise ArgumentError, "Failed to read opcode of the script chunk" if !opcode
148
+
149
+ # push data opcode
150
+ if opcode <= OP_PUSHDATA4
151
+
152
+ length = data.bytesize
153
+
154
+ if opcode < OP_PUSHDATA1
155
+ pushdata_length = opcode
156
+ chunk_length = 1 + pushdata_length
157
+ if offset + chunk_length > length
158
+ raise ArgumentError, "PUSHDATA is longer than we have bytes available"
159
+ end
160
+ return self.new(data[offset, chunk_length])
161
+ elsif opcode == OP_PUSHDATA1
162
+ pushdata_length, _ = BTC::WireFormat.read_uint8(data: data, offset: offset + 1)
163
+ if !pushdata_length
164
+ raise ArgumentError, "Failed to read length for PUSHDATA1"
165
+ end
166
+ chunk_length = 1 + 1 + pushdata_length
167
+ if offset + chunk_length > length
168
+ raise ArgumentError, "PUSHDATA1 is longer than we have bytes available"
169
+ end
170
+ return self.new(data[offset, chunk_length])
171
+ elsif (opcode == OP_PUSHDATA2)
172
+ pushdata_length, _ = BTC::WireFormat.read_uint16le(data: data, offset: offset + 1)
173
+ if !pushdata_length
174
+ raise ArgumentError, "Failed to read length for PUSHDATA2"
175
+ end
176
+ chunk_length = 1 + 2 + pushdata_length
177
+ if offset + chunk_length > length
178
+ raise ArgumentError, "PUSHDATA2 is longer than we have bytes available"
179
+ end
180
+ return self.new(data[offset, chunk_length])
181
+ elsif (opcode == OP_PUSHDATA4)
182
+ pushdata_length, _ = BTC::WireFormat.read_uint32le(data: data, offset: offset + 1)
183
+ if !pushdata_length
184
+ raise ArgumentError, "Failed to read length for PUSHDATA4"
185
+ end
186
+ chunk_length = 1 + 4 + pushdata_length
187
+ if offset + chunk_length > length
188
+ raise ArgumentError, "PUSHDATA4 is longer than we have bytes available"
189
+ end
190
+ return self.new(data[offset, chunk_length])
191
+ end
192
+ else
193
+ # simple opcode - 1 byte
194
+ return self.new(data[offset, 1])
195
+ end
196
+ end
197
+
198
+ def initialize(raw_data)
199
+ @raw_data = raw_data
200
+ end
201
+
202
+ def ==(other)
203
+ @raw_data == other.raw_data
204
+ end
205
+
206
+ protected
207
+
208
+ def data_is_ascii_printable?(data)
209
+ data.each_byte do |byte|
210
+ return false if !(byte >= 0x20 && byte <= 0x7E)
211
+ end
212
+ return true
213
+ end
214
+
215
+ end # ScriptChunk
216
+ end
@@ -1,3 +1,3 @@
1
1
  module BTC
2
- VERSION = "1.1.3".freeze
2
+ VERSION = "1.1.4".freeze
3
3
  end
@@ -71,7 +71,7 @@ describe BTC::Script do
71
71
  end
72
72
 
73
73
 
74
- it "removing subscript does not modify receiver" do
74
+ it "removing subscript does not modify the receiver" do
75
75
  s = Script.new << OP_DUP << OP_HASH160 << OP_CODESEPARATOR << "some data" << OP_EQUALVERIFY << OP_CHECKSIG
76
76
  s1 = s.dup
77
77
  s2 = s1.find_and_delete(Script.new << OP_HASH160)
@@ -91,4 +91,10 @@ describe BTC::Script do
91
91
  s2.must_equal(Script.new.append_pushdata("foo", opcode:OP_PUSHDATA1))
92
92
  end
93
93
 
94
+ it "should parse interpreted data and pushdata correctly" do
95
+ script = Script.new << OP_0 << OP_1NEGATE << OP_NOP << OP_RESERVED << OP_1 << OP_16 << "1" << "2" << "chancellor"
96
+ script.chunks.map{|c| c.interpreted_data }.must_equal ["", "\x81".b, nil, nil, "\x01".b, "\x10".b, "1", "2", "chancellor"]
97
+ script.chunks.map{|c| c.pushdata }.must_equal ["", nil, nil, nil, nil, nil, "1", "2", "chancellor"]
98
+ end
99
+
94
100
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: btcruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 1.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oleg Andreev
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-08-17 00:00:00.000000000 Z
12
+ date: 2015-08-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ffi
@@ -108,6 +108,7 @@ files:
108
108
  - lib/btcruby/script/opcode.rb
109
109
  - lib/btcruby/script/p2sh_plugin.rb
110
110
  - lib/btcruby/script/script.rb
111
+ - lib/btcruby/script/script_chunk.rb
111
112
  - lib/btcruby/script/script_error.rb
112
113
  - lib/btcruby/script/script_flags.rb
113
114
  - lib/btcruby/script/script_interpreter.rb