btcruby 1.0.9 → 1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,254 @@
1
+ module BTC
2
+ # (Based on CScriptNum)
3
+ # Numeric opcodes (OP_1ADD, etc) are restricted to operating on 4-byte integers.
4
+ # The semantics are subtle, though: operands must be in the range [-2^31 +1...2^31 -1],
5
+ # but results may overflow (and are valid as long as they are not used in a subsequent
6
+ # numeric operation). ScriptNumber enforces those semantics by storing results as
7
+ # an int64 and allowing out-of-range values to be returned as a vector of bytes but
8
+ # throwing an exception if arithmetic is done or the result is interpreted as an integer.
9
+ class ScriptNumberError < ArgumentError; end
10
+
11
+ class ScriptNumber
12
+ DEFAULT_MAX_SIZE = 4
13
+
14
+ INT64_MAX = 0x7fffffffffffffff
15
+ INT64_MIN = -INT64_MAX - 1
16
+
17
+ def initialize(integer: nil, boolean: nil, data: nil, hex: nil, require_minimal: true, max_size: DEFAULT_MAX_SIZE)
18
+ if integer
19
+ assert(integer >= INT64_MIN && integer <= INT64_MAX, "Integer must be within int64 range")
20
+ @integer = integer
21
+ elsif data || hex
22
+ data ||= BTC.from_hex(hex)
23
+ if data.bytesize > max_size
24
+ raise ScriptNumberError, "script number overflow (#{data.bytesize} > #{max_size})"
25
+ end
26
+ if require_minimal && data.bytesize > 0
27
+ # Check that the number is encoded with the minimum possible
28
+ # number of bytes.
29
+ #
30
+ # If the most-significant-byte - excluding the sign bit - is zero
31
+ # then we're not minimal. Note how this test also rejects the
32
+ # negative-zero encoding, 0x80.
33
+ if (data.bytes.last & 0x7f) == 0
34
+ # One exception: if there's more than one byte and the most
35
+ # significant bit of the second-most-significant-byte is set
36
+ # it would conflict with the sign bit. An example of this case
37
+ # is +-255, which encode to 0xff00 and 0xff80 respectively.
38
+ # (big-endian).
39
+ if data.bytesize <= 1 || (data.bytes[data.bytesize - 2] & 0x80) == 0
40
+ raise ScriptNumberError, "non-minimally encoded script number"
41
+ end
42
+ end
43
+ end
44
+ @integer = self.class.decode_integer(data)
45
+ elsif boolean == false || boolean == true
46
+ @integer = boolean ? 1 : 0
47
+ else
48
+ raise ArgumentError
49
+ end
50
+ end
51
+
52
+ # Operators
53
+
54
+ def ==(other); @integer == other.to_i; end
55
+ def !=(other); @integer != other.to_i; end
56
+ def <=(other); @integer <= other.to_i; end
57
+ def <(other); @integer < other.to_i; end
58
+ def >=(other); @integer >= other.to_i; end
59
+ def >(other); @integer > other.to_i; end
60
+
61
+ def +(other); self.class.new(integer: @integer + other.to_i); end
62
+ def -(other); self.class.new(integer: @integer - other.to_i); end
63
+
64
+ def +@
65
+ self
66
+ end
67
+
68
+ def -@
69
+ assert(@integer > INT64_MIN && @integer <= INT64_MAX, "Integer will not be within int64 range after negation")
70
+ self.class.new(integer: -@integer)
71
+ end
72
+
73
+
74
+
75
+ # Conversion Methods
76
+
77
+ def to_i
78
+ @integer
79
+ end
80
+
81
+ def to_s
82
+ @integer.to_s
83
+ end
84
+
85
+ def data
86
+ self.class.encode_integer(@integer)
87
+ end
88
+
89
+ def to_hex
90
+ BTC.to_hex(data)
91
+ end
92
+
93
+ def self.decode_integer(data)
94
+ return 0 if data.empty?
95
+
96
+ result = 0
97
+
98
+ bytes = data.bytes
99
+ bytes.each_with_index do |byte, i|
100
+ result |= bytes[i] << 8*i
101
+ end
102
+
103
+ # If the input vector's most significant byte is 0x80, remove it from
104
+ # the result's msb and return a negative.
105
+ if (bytes.last & 0x80) != 0
106
+ return -(result & ~(0x80 << (8 * (bytes.size - 1))));
107
+ end
108
+ return result
109
+ end
110
+
111
+ def self.encode_integer(value)
112
+ return "".b if value == 0
113
+
114
+ result = []
115
+ negative = value < 0
116
+ absvalue = negative ? -value : value
117
+
118
+ while absvalue != 0
119
+ result.push(absvalue & 0xff)
120
+ absvalue >>= 8
121
+ end
122
+
123
+ # - If the most significant byte is >= 0x80 and the value is positive, push a
124
+ # new zero-byte to make the significant byte < 0x80 again.
125
+ #
126
+ # - If the most significant byte is >= 0x80 and the value is negative, push a
127
+ # new 0x80 byte that will be popped off when converting to an integral.
128
+ #
129
+ # - If the most significant byte is < 0x80 and the value is negative, add
130
+ # 0x80 to it, since it will be subtracted and interpreted as a negative when
131
+ # converting to an integral.
132
+
133
+ if (result.last & 0x80) != 0
134
+ result.push(negative ? 0x80 : 0)
135
+ elsif negative
136
+ result[result.size - 1] = result.last | 0x80
137
+ end
138
+
139
+ BTC.data_from_bytes(result)
140
+ end
141
+
142
+ def assert(condition, message)
143
+ raise message if !condition
144
+ end
145
+
146
+ end
147
+ end
148
+
149
+ if $0 == __FILE__
150
+ require 'btcruby'
151
+
152
+ include BTC
153
+ def run_tests
154
+
155
+ # Decoding
156
+
157
+ [-1000000000000000,-10000,-100,-1,0,1,10,1000,100000000000000].each do |i|
158
+ should_equal(ScriptNumber.new(integer: i).to_i, i, "Must return integer as-is.")
159
+ end
160
+
161
+ should_equal(ScriptNumber.new(data: "").to_i, 0, "Must parse empty string as zero.")
162
+ should_equal(ScriptNumber.new(data: "\x01").to_i, 1, "Must parse 0x01 as 1.")
163
+ should_equal(ScriptNumber.new(data: "\xff").to_i, -127, "Must parse 0xff as -127.")
164
+ should_equal(ScriptNumber.new(data: "\xff\x00").to_i, 255, "Must parse 0xff00 as 255.")
165
+ should_equal(ScriptNumber.new(data: "\x81").to_i, -1, "Must parse 0x81 as -1.")
166
+ should_equal(ScriptNumber.new(data: "\x8f").to_i, -15, "Must parse 0x8f as -0x0f.")
167
+ should_equal(ScriptNumber.new(data: "\x00\x81").to_i, -256, "Must parse 0x0081 as -256.")
168
+ should_equal(ScriptNumber.new(data: "\xff\x80").to_i, -255, "Must decode -255.")
169
+
170
+ should_raise('non-minimally encoded script number') { ScriptNumber.new(data: "\x00") }
171
+ should_raise('non-minimally encoded script number') { ScriptNumber.new(data: "\x80") }
172
+ should_raise('non-minimally encoded script number') { ScriptNumber.new(data: "\x00\x80") }
173
+ should_raise('non-minimally encoded script number') { ScriptNumber.new(data: "\x01\x80") }
174
+ should_raise('non-minimally encoded script number') { ScriptNumber.new(data: "\x00\x00\x80") }
175
+ should_raise('non-minimally encoded script number') { ScriptNumber.new(data: "\x00\x10\x80") }
176
+ should_raise('non-minimally encoded script number') { ScriptNumber.new(data: "\x10\x00\x80") }
177
+ should_raise('script number overflow (3 > 2)') { ScriptNumber.new(data: "\x00\x00\x80", max_size: 2) }
178
+
179
+ # Encoding
180
+
181
+ should_equal(ScriptNumber.new(integer: 0).to_hex, "")
182
+ should_equal(ScriptNumber.new(integer: 1).to_hex, "01")
183
+ should_equal(ScriptNumber.new(integer: -1).to_hex, "81")
184
+ should_equal(ScriptNumber.new(integer: 255).to_hex, "ff00")
185
+ should_equal(ScriptNumber.new(integer: -255).to_hex, "ff80")
186
+ should_equal(ScriptNumber.new(integer: 0xffff).to_hex, "ffff00")
187
+ should_equal(ScriptNumber.new(integer: -0xffff).to_hex, "ffff80")
188
+
189
+ # Back and forth test
190
+
191
+ (-100000..10000).each do |i|
192
+ d = ScriptNumber.new(integer: i).data
193
+ #puts BTC.to_hex(d) if i % 16 == 0
194
+ i2 = ScriptNumber.new(data: d).to_i
195
+ should_equal(i, i2)
196
+ end
197
+
198
+ # Booleans
199
+
200
+ should_equal(ScriptNumber.new(boolean: true), 1)
201
+ should_equal(ScriptNumber.new(boolean: false), 0)
202
+
203
+ # Equality
204
+
205
+ should_equal(ScriptNumber.new(integer: 0) == 0, true)
206
+ should_equal(ScriptNumber.new(integer: 1) == 1, true)
207
+ should_equal(ScriptNumber.new(integer: -1) == -1, true)
208
+
209
+ should_equal(ScriptNumber.new(integer: 0) == ScriptNumber.new(integer: 0), true)
210
+ should_equal(ScriptNumber.new(integer: 1) == ScriptNumber.new(integer: 1), true)
211
+ should_equal(ScriptNumber.new(integer: -1) == ScriptNumber.new(integer: -1), true)
212
+
213
+ should_equal(ScriptNumber.new(integer: 0) != 0, false)
214
+ should_equal(ScriptNumber.new(integer: 1) != 1, false)
215
+ should_equal(ScriptNumber.new(integer: -1) != -1, false)
216
+
217
+ should_equal(ScriptNumber.new(integer: 0) != ScriptNumber.new(integer: 0), false)
218
+ should_equal(ScriptNumber.new(integer: 1) != ScriptNumber.new(integer: 1), false)
219
+ should_equal(ScriptNumber.new(integer: -1) != ScriptNumber.new(integer: -1), false)
220
+
221
+ # Arithmetic
222
+
223
+ sn = ScriptNumber.new(integer: 123)
224
+ sn -= 1
225
+ should_equal(sn, 122)
226
+
227
+ puts "All tests passed."
228
+ end
229
+
230
+ def should_equal(a, b, msg = 'Must equal')
231
+ a == b or raise "#{msg} Expected #{b.inspect}, received #{a.inspect}."
232
+ end
233
+
234
+ def should_raise(message)
235
+ raised = false
236
+ begin
237
+ yield
238
+ rescue => e
239
+ if e.message == message
240
+ raised = true
241
+ else
242
+ raise "Raised unexpected exception: #{e}"
243
+ end
244
+ end
245
+ if !raised
246
+ raise "Should have raised #{message.inspect}!"
247
+ end
248
+ end
249
+
250
+ run_tests
251
+ end
252
+
253
+
254
+
@@ -0,0 +1,14 @@
1
+ module BTC
2
+ # Protocol for signature checkers
3
+ module SignatureChecker
4
+ # Returns a boolean indicating if signature is valid for the given public key
5
+ def check_signature(script_signature: nil, public_key:nil, script:nil)
6
+ false
7
+ end
8
+ # Returns a boolean indicating if lock time is valid.
9
+ # lock_time is ScriptNumber instance.
10
+ def check_lock_time(lock_time)
11
+ false
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,53 @@
1
+ module BTC
2
+ class TestSignatureChecker
3
+ include SignatureChecker
4
+
5
+ def initialize(signature_hash: nil, timestamp: nil)
6
+ @signature_hash = signature_hash
7
+ @timestamp = timestamp
8
+ end
9
+
10
+ # for testing check_signature
11
+ def signature_hash
12
+ @signature_hash
13
+ end
14
+
15
+ # for testing check_lock_time
16
+ def timestamp
17
+ @timestamp
18
+ end
19
+
20
+ def check_signature(script_signature:nil, public_key:nil, script:nil)
21
+ # Signature must be long enough to contain a sighash byte.
22
+ return false if script_signature.size < 1
23
+
24
+ # Extract raw ECDSA signature by stripping off the last hashtype byte
25
+ ecdsa_sig = script_signature[0..-2]
26
+
27
+ key = BTC::Key.new(public_key: public_key)
28
+ result = key.verify_ecdsa_signature(ecdsa_sig, hash)
29
+ return result
30
+ end
31
+
32
+ def check_lock_time(lock_time)
33
+ # There are two times of nLockTime: lock-by-blockheight
34
+ # and lock-by-blocktime, distinguished by whether
35
+ # nLockTime < LOCKTIME_THRESHOLD.
36
+ #
37
+ # We want to compare apples to apples, so fail the script
38
+ # if the type of nLockTime being tested is not the same as
39
+ # the nLockTime in the transaction.
40
+ if !(
41
+ (timestamp < LOCKTIME_THRESHOLD && lock_time < LOCKTIME_THRESHOLD) ||
42
+ (timestamp >= LOCKTIME_THRESHOLD && lock_time >= LOCKTIME_THRESHOLD)
43
+ )
44
+ return false
45
+ end
46
+
47
+ # Now that we know we're comparing apples-to-apples, the
48
+ # comparison is a simple numeric one.
49
+ return timestamp >= lock_time
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,66 @@
1
+ module BTC
2
+ class TransactionSignatureChecker
3
+ include SignatureChecker
4
+
5
+ attr_accessor :transaction
6
+ attr_accessor :input_index
7
+ def initialize(transaction: nil, input_index: nil)
8
+ @transaction = transaction
9
+ @input_index = input_index
10
+ end
11
+
12
+ def check_signature(script_signature:nil, public_key:nil, script:nil)
13
+ # Signature must be long enough to contain a sighash byte.
14
+ return false if script_signature.size < 1
15
+
16
+ hashtype = script_signature[-1].bytes.first
17
+
18
+ # Extract raw ECDSA signature by stripping off the last hashtype byte
19
+ ecdsa_sig = script_signature[0..-2]
20
+
21
+ key = BTC::Key.new(public_key: public_key)
22
+ hash = @transaction.signature_hash(input_index: @input_index, output_script: script, hash_type: hashtype)
23
+ result = key.verify_ecdsa_signature(ecdsa_sig, hash)
24
+ return result
25
+ end
26
+
27
+ def check_lock_time(lock_time)
28
+ # There are two times of nLockTime: lock-by-blockheight
29
+ # and lock-by-blocktime, distinguished by whether
30
+ # nLockTime < LOCKTIME_THRESHOLD.
31
+ #
32
+ # We want to compare apples to apples, so fail the script
33
+ # if the type of nLockTime being tested is not the same as
34
+ # the nLockTime in the transaction.
35
+ if !(
36
+ (@transaction.lock_time < LOCKTIME_THRESHOLD && lock_time < LOCKTIME_THRESHOLD) ||
37
+ (@transaction.lock_time >= LOCKTIME_THRESHOLD && lock_time >= LOCKTIME_THRESHOLD)
38
+ )
39
+ return false
40
+ end
41
+
42
+ # Now that we know we're comparing apples-to-apples, the
43
+ # comparison is a simple numeric one.
44
+ if lock_time > @transaction.lock_time
45
+ return false
46
+ end
47
+
48
+ # Finally the nLockTime feature can be disabled and thus
49
+ # CHECKLOCKTIMEVERIFY bypassed if every txin has been
50
+ # finalized by setting nSequence to maxint. The
51
+ # transaction would be allowed into the blockchain, making
52
+ # the opcode ineffective.
53
+ #
54
+ # Testing if this vin is not final is sufficient to
55
+ # prevent this condition. Alternatively we could test all
56
+ # inputs, but testing just this input minimizes the data
57
+ # required to prove correct CHECKLOCKTIMEVERIFY execution.
58
+ if @transaction.inputs[@input_index].final?
59
+ return false
60
+ end
61
+
62
+ return true
63
+ end
64
+
65
+ end
66
+ end
@@ -78,7 +78,7 @@ module BTC
78
78
  @previous_hash = previous_hash || ZERO_HASH256
79
79
  @previous_hash = BTC.hash_from_id(previous_id) if previous_id
80
80
  @previous_index = previous_index || INVALID_INDEX
81
- @coinbase_data = coinbase_data
81
+ @coinbase_data = coinbase_data || "".b
82
82
  @signature_script = signature_script || BTC::Script.new
83
83
  @sequence = sequence || MAX_SEQUENCE
84
84
  end
@@ -1,3 +1,3 @@
1
1
  module BTC
2
- VERSION = "1.0.9".freeze
2
+ VERSION = "1.1".freeze
3
3
  end
@@ -184,12 +184,44 @@ module BTC
184
184
 
185
185
  intbuf + stringbuf
186
186
  end
187
-
188
-
187
+
188
+
189
+ # Reads varint length prefix, then calls the block appropriate number of times to read the items.
190
+ # Returns an array of items.
191
+ def read_array(data: nil, stream: nil, offset: 0)
192
+ count, len = read_varint(data: data, stream: stream, offset: offset)
193
+ return [nil, len] if !count
194
+ (0...count).map do |i|
195
+ yield
196
+ end
197
+ end
198
+
199
+ def encode_array(array, &block)
200
+ write_array(array, &block)
201
+ end
202
+
203
+ def write_array(array, data: nil, stream: nil, &block)
204
+ raise ArgumentError, "Array must be present" if !array
205
+ raise ArgumentError, "Parsing block must be present" if !block
206
+ intbuf = write_varint(array.size, data: data, stream: stream)
207
+ array.inject(intbuf) do |buf, e|
208
+ string = block.call(e)
209
+ stringbuf = BTC::Data.ensure_binary_encoding(string)
210
+ data << stringbuf if data
211
+ stream.write(stringbuf) if stream
212
+ buf << stringbuf
213
+ buf
214
+ end
215
+ end
216
+
217
+
218
+
219
+
220
+
189
221
  # LEB128 encoding used in Open Assets protocol
190
-
222
+
191
223
  # Decodes an unsigned integer encoded in LEB128.
192
- # Returns `[value, length]` where `value` is an integer decoded from LEB128 and `length`
224
+ # Returns `[value, length]` where `value` is an integer decoded from LEB128 and `length`
193
225
  # is a number of bytes read (includes length prefix and offset bytes).
194
226
  def read_uleb128(data: nil, stream: nil, offset: 0)
195
227
  if (data && stream) || (!data && !stream)
@@ -252,8 +284,8 @@ module BTC
252
284
  stream.write(buf) if stream
253
285
  buf
254
286
  end
255
-
256
-
287
+
288
+
257
289
 
258
290
  PACK_FORMAT_UINT8 = "C".freeze
259
291
  PACK_FORMAT_INT8 = "c".freeze
@@ -291,11 +323,11 @@ module BTC
291
323
  def read_int32le(data: nil, stream: nil, offset: 0)
292
324
  _read_fixint(name: :int32le, length: 4, pack_format: PACK_FORMAT_INT32LE, data: data, stream: stream, offset: offset)
293
325
  end
294
-
326
+
295
327
  def read_uint32be(data: nil, stream: nil, offset: 0) # used in BIP32
296
328
  _read_fixint(name: :uint32be, length: 4, pack_format: PACK_FORMAT_UINT32BE, data: data, stream: stream, offset: offset)
297
329
  end
298
-
330
+
299
331
  def read_int32be(data: nil, stream: nil, offset: 0)
300
332
  _read_fixint(name: :int32be, length: 4, pack_format: PACK_FORMAT_INT32BE, data: data, stream: stream, offset: offset)
301
333
  end