btcruby 1.1.3 → 1.1.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
  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