cryptocoin 0.0.1b
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 +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +85 -0
- data/Rakefile +2 -0
- data/cryptocoin.gemspec +24 -0
- data/lib/cryptocoin/core_ext/integer.rb +24 -0
- data/lib/cryptocoin/core_ext/string.rb +30 -0
- data/lib/cryptocoin/digest.rb +36 -0
- data/lib/cryptocoin/merkle_tree.rb +78 -0
- data/lib/cryptocoin/network/bitcoin.rb +9 -0
- data/lib/cryptocoin/network/dogecoin.rb +9 -0
- data/lib/cryptocoin/network/litecoin.rb +9 -0
- data/lib/cryptocoin/network.rb +53 -0
- data/lib/cryptocoin/protocol/block_header.rb +58 -0
- data/lib/cryptocoin/protocol/inventory_vector.rb +28 -0
- data/lib/cryptocoin/protocol/message/addr.rb +29 -0
- data/lib/cryptocoin/protocol/message/alert.rb +0 -0
- data/lib/cryptocoin/protocol/message/block.rb +13 -0
- data/lib/cryptocoin/protocol/message/getaddr.rb +17 -0
- data/lib/cryptocoin/protocol/message/getblocks.rb +44 -0
- data/lib/cryptocoin/protocol/message/getdata.rb +31 -0
- data/lib/cryptocoin/protocol/message/getheaders.rb +44 -0
- data/lib/cryptocoin/protocol/message/headers.rb +30 -0
- data/lib/cryptocoin/protocol/message/inv.rb +31 -0
- data/lib/cryptocoin/protocol/message/mempool.rb +16 -0
- data/lib/cryptocoin/protocol/message/notfound.rb +31 -0
- data/lib/cryptocoin/protocol/message/ping.rb +24 -0
- data/lib/cryptocoin/protocol/message/pong.rb +24 -0
- data/lib/cryptocoin/protocol/message/reject.rb +54 -0
- data/lib/cryptocoin/protocol/message/tx.rb +11 -0
- data/lib/cryptocoin/protocol/message/verack.rb +16 -0
- data/lib/cryptocoin/protocol/message/version.rb +59 -0
- data/lib/cryptocoin/protocol/message.rb +27 -0
- data/lib/cryptocoin/protocol/net_addr.rb +55 -0
- data/lib/cryptocoin/protocol/packet.rb +74 -0
- data/lib/cryptocoin/protocol/var_len_int.rb +85 -0
- data/lib/cryptocoin/protocol/var_len_str.rb +18 -0
- data/lib/cryptocoin/protocol.rb +8 -0
- data/lib/cryptocoin/script/op_code/constants.rb +157 -0
- data/lib/cryptocoin/script/op_code/functions.rb +515 -0
- data/lib/cryptocoin/script/op_code.rb +59 -0
- data/lib/cryptocoin/script.rb +234 -0
- data/lib/cryptocoin/structure/address.rb +64 -0
- data/lib/cryptocoin/structure/block.rb +109 -0
- data/lib/cryptocoin/structure/key_pair.rb +57 -0
- data/lib/cryptocoin/structure/merkle_branch.rb +37 -0
- data/lib/cryptocoin/structure/transaction/input.rb +80 -0
- data/lib/cryptocoin/structure/transaction/output.rb +49 -0
- data/lib/cryptocoin/structure/transaction.rb +94 -0
- data/lib/cryptocoin/version.rb +3 -0
- data/lib/cryptocoin.rb +29 -0
- data/spec/script_spec.rb +42 -0
- data/spec/spec_helper.rb +20 -0
- metadata +145 -0
@@ -0,0 +1,157 @@
|
|
1
|
+
module Cryptocoin
|
2
|
+
class Script
|
3
|
+
# Taken from the Bitcoin official client
|
4
|
+
class OpCode
|
5
|
+
module Constants
|
6
|
+
# push value onto stack
|
7
|
+
OP_0 = 0x00
|
8
|
+
OP_FALSE = OP_0
|
9
|
+
OP_PUSHDATA0 = 0x01
|
10
|
+
OP_PUSHDATA1 = 0x4c
|
11
|
+
OP_PUSHDATA2 = 0x4d
|
12
|
+
OP_PUSHDATA4 = 0x4e
|
13
|
+
OP_1NEGATE = 0x4f
|
14
|
+
OP_RESERVED = 0x50
|
15
|
+
OP_1 = 0x51
|
16
|
+
OP_TRUE = OP_1
|
17
|
+
OP_2 = 0x52
|
18
|
+
OP_3 = 0x53
|
19
|
+
OP_4 = 0x54
|
20
|
+
OP_5 = 0x55
|
21
|
+
OP_6 = 0x56
|
22
|
+
OP_7 = 0x57
|
23
|
+
OP_8 = 0x58
|
24
|
+
OP_9 = 0x59
|
25
|
+
OP_10 = 0x5a
|
26
|
+
OP_11 = 0x5b
|
27
|
+
OP_12 = 0x5c
|
28
|
+
OP_13 = 0x5d
|
29
|
+
OP_14 = 0x5e
|
30
|
+
OP_15 = 0x5f
|
31
|
+
OP_16 = 0x60
|
32
|
+
|
33
|
+
# control
|
34
|
+
OP_NOP = 0x61
|
35
|
+
OP_VER = 0x62
|
36
|
+
OP_IF = 0x63
|
37
|
+
OP_NOTIF = 0x64
|
38
|
+
OP_VERIF = 0x65
|
39
|
+
OP_VERNOTIF = 0x66
|
40
|
+
OP_ELSE = 0x67
|
41
|
+
OP_ENDIF = 0x68
|
42
|
+
OP_VERIFY = 0x69
|
43
|
+
OP_RETURN = 0x6a
|
44
|
+
|
45
|
+
# stack ops
|
46
|
+
OP_TOALTSTACK = 0x6b
|
47
|
+
OP_FROMALTSTACK = 0x6c
|
48
|
+
OP_2DROP = 0x6d
|
49
|
+
OP_2DUP = 0x6e
|
50
|
+
OP_3DUP = 0x6f
|
51
|
+
OP_2OVER = 0x70
|
52
|
+
OP_2ROT = 0x71
|
53
|
+
OP_2SWAP = 0x72
|
54
|
+
OP_IFDUP = 0x73
|
55
|
+
OP_DEPTH = 0x74
|
56
|
+
OP_DROP = 0x75
|
57
|
+
OP_DUP = 0x76
|
58
|
+
OP_NIP = 0x77
|
59
|
+
OP_OVER = 0x78
|
60
|
+
OP_PICK = 0x79
|
61
|
+
OP_ROLL = 0x7a
|
62
|
+
OP_ROT = 0x7b
|
63
|
+
OP_SWAP = 0x7c
|
64
|
+
OP_TUCK = 0x7d
|
65
|
+
|
66
|
+
# splice ops
|
67
|
+
OP_CAT = 0x7e
|
68
|
+
OP_SUBSTR = 0x7f
|
69
|
+
OP_LEFT = 0x80
|
70
|
+
OP_RIGHT = 0x81
|
71
|
+
OP_SIZE = 0x82
|
72
|
+
|
73
|
+
# bit logic
|
74
|
+
OP_INVERT = 0x83
|
75
|
+
OP_AND = 0x84
|
76
|
+
OP_OR = 0x85
|
77
|
+
OP_XOR = 0x86
|
78
|
+
OP_EQUAL = 0x87
|
79
|
+
OP_EQUALVERIFY = 0x88
|
80
|
+
OP_RESERVED1 = 0x89
|
81
|
+
OP_RESERVED2 = 0x8a
|
82
|
+
|
83
|
+
# numeric
|
84
|
+
OP_1ADD = 0x8b
|
85
|
+
OP_1SUB = 0x8c
|
86
|
+
OP_2MUL = 0x8d
|
87
|
+
OP_2DIV = 0x8e
|
88
|
+
OP_NEGATE = 0x8f
|
89
|
+
OP_ABS = 0x90
|
90
|
+
OP_NOT = 0x91
|
91
|
+
OP_0NOTEQUAL = 0x92
|
92
|
+
|
93
|
+
OP_ADD = 0x93
|
94
|
+
OP_SUB = 0x94
|
95
|
+
OP_MUL = 0x95
|
96
|
+
OP_DIV = 0x96
|
97
|
+
OP_MOD = 0x97
|
98
|
+
OP_LSHIFT = 0x98
|
99
|
+
OP_RSHIFT = 0x99
|
100
|
+
|
101
|
+
OP_BOOLAND = 0x9a
|
102
|
+
OP_BOOLOR = 0x9b
|
103
|
+
OP_NUMEQUAL = 0x9c
|
104
|
+
OP_NUMEQUALVERIFY = 0x9d
|
105
|
+
OP_NUMNOTEQUAL = 0x9e
|
106
|
+
OP_LESSTHAN = 0x9f
|
107
|
+
OP_GREATERTHAN = 0xa0
|
108
|
+
OP_LESSTHANOREQUAL = 0xa1
|
109
|
+
OP_GREATERTHANOREQUAL = 0xa2
|
110
|
+
OP_MIN = 0xa3
|
111
|
+
OP_MAX = 0xa4
|
112
|
+
|
113
|
+
OP_WITHIN = 0xa5
|
114
|
+
|
115
|
+
# crypto
|
116
|
+
OP_RIPEMD160 = 0xa6
|
117
|
+
OP_SHA1 = 0xa7
|
118
|
+
OP_SHA256 = 0xa8
|
119
|
+
OP_HASH160 = 0xa9
|
120
|
+
OP_HASH256 = 0xaa
|
121
|
+
OP_CODESEPARATOR = 0xab
|
122
|
+
OP_CHECKSIG = 0xac
|
123
|
+
OP_CHECKSIGVERIFY = 0xad
|
124
|
+
OP_CHECKMULTISIG = 0xae
|
125
|
+
OP_CHECKMULTISIGVERIFY = 0xaf
|
126
|
+
|
127
|
+
# expansion
|
128
|
+
OP_NOP1 = 0xb0
|
129
|
+
OP_NOP2 = 0xb1
|
130
|
+
OP_NOP3 = 0xb2
|
131
|
+
OP_NOP4 = 0xb3
|
132
|
+
OP_NOP5 = 0xb4
|
133
|
+
OP_NOP6 = 0xb5
|
134
|
+
OP_NOP7 = 0xb6
|
135
|
+
OP_NOP8 = 0xb7
|
136
|
+
OP_NOP9 = 0xb8
|
137
|
+
OP_NOP10 = 0xb9
|
138
|
+
|
139
|
+
# template matching params
|
140
|
+
OP_SMALLDATA = 0xf9
|
141
|
+
OP_SMALLINTEGER = 0xfa
|
142
|
+
OP_PUBKEYS = 0xfb
|
143
|
+
OP_PUBKEYHASH = 0xfd
|
144
|
+
OP_PUBKEY = 0xfe
|
145
|
+
|
146
|
+
OP_INVALIDOPCODE = 0xff
|
147
|
+
|
148
|
+
def self.const_get(*args)
|
149
|
+
super(*args)
|
150
|
+
rescue NameError => msg
|
151
|
+
return OP_PUSHDATA0 if args[0] == "OP_PUSHDATA0"
|
152
|
+
raise NameError, msg
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,515 @@
|
|
1
|
+
require 'cryptocoin/core_ext/integer'
|
2
|
+
require 'cryptocoin/core_ext/string'
|
3
|
+
require 'cryptocoin/script/op_code/constants'
|
4
|
+
require 'digest/sha2'
|
5
|
+
require 'digest/rmd160'
|
6
|
+
|
7
|
+
module Cryptocoin
|
8
|
+
class Script
|
9
|
+
class OpCode
|
10
|
+
module Functions
|
11
|
+
include Cryptocoin::Script::OpCode::Constants
|
12
|
+
|
13
|
+
# One of a kind function
|
14
|
+
# Sends back the stack, modified string, raw, seek
|
15
|
+
def op_pushdata(stack, opcode, script, script_string, raw, seek)
|
16
|
+
bytes = case opcode.hex
|
17
|
+
when OP_PUSHDATA0..OP_PUSHDATA1
|
18
|
+
opcode.raw
|
19
|
+
when OP_PUSHDATA1
|
20
|
+
seek+=1
|
21
|
+
script[seek-1]
|
22
|
+
when OP_PUSHDATA2
|
23
|
+
seek+=2
|
24
|
+
script[seek-2..seek]
|
25
|
+
when OP_PUSHDATA4
|
26
|
+
seek+=4
|
27
|
+
script[seek-4..seek]
|
28
|
+
end
|
29
|
+
|
30
|
+
bytes = bytes.unpack('H*')[0].to_i(16)
|
31
|
+
|
32
|
+
unless OP_PUSHDATA0..OP_PUSHDATA1 === opcode.hex
|
33
|
+
raw += [bytes].pack('H*')
|
34
|
+
script_string.push(bytes)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Read the value from the bytes and see how much to read
|
38
|
+
# return nil if invalid according to spec
|
39
|
+
return false if bytes > 520
|
40
|
+
j = script[seek..seek+bytes-1]
|
41
|
+
script_string.push(j.unpack('H*')[0])
|
42
|
+
raw += j
|
43
|
+
stack.push(j)
|
44
|
+
seek+=bytes
|
45
|
+
[stack, script_string, raw, seek]
|
46
|
+
end
|
47
|
+
|
48
|
+
def op_numeric(stack, opcode)
|
49
|
+
stack.push((opcode.hex - (OP_1 - 1)).to_openssl_bn)
|
50
|
+
end
|
51
|
+
|
52
|
+
def op_if(stack, exec_stack, opcode)
|
53
|
+
require_stack stack do
|
54
|
+
val = false
|
55
|
+
i = stack.pop
|
56
|
+
if !!i.to_openssl_bn_int
|
57
|
+
val = true if opcode.hex == OP_IF
|
58
|
+
else
|
59
|
+
val = true if opcode.hex == OP_NOTIF
|
60
|
+
end
|
61
|
+
exec_stack.push(val)
|
62
|
+
[stack, exec_stack]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def op_else(exec_stack)
|
67
|
+
require_stack exec_stack do
|
68
|
+
exec_stack[-1] = !exec_stack[-1]
|
69
|
+
exec_stack
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def op_endif(exec_stack)
|
74
|
+
require_stack exec_stack do
|
75
|
+
exec_stack.pop
|
76
|
+
exec_stack
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def op_verify(stack, transaction_valid)
|
81
|
+
require_stack stack do
|
82
|
+
i = stack.pop
|
83
|
+
if i.to_openssl_bn_int == 0
|
84
|
+
stack.push(i)
|
85
|
+
transaction_valid = false
|
86
|
+
end
|
87
|
+
[stack, transaction_valid]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def op_toaltstack(stack, alt_stack)
|
92
|
+
require_stack stack do
|
93
|
+
alt_stack.push(stack.pop)
|
94
|
+
end
|
95
|
+
[stack, alt_stack]
|
96
|
+
end
|
97
|
+
|
98
|
+
def op_fromaltstack(stack, alt_stack)
|
99
|
+
require_stack alt_stack do
|
100
|
+
stack.push(alt_stack.pop)
|
101
|
+
end
|
102
|
+
[stack, alt_stack]
|
103
|
+
end
|
104
|
+
|
105
|
+
def op_2drop(stack)
|
106
|
+
require_stack stack, 2 do
|
107
|
+
2.times do
|
108
|
+
stack.pop
|
109
|
+
end
|
110
|
+
stack
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def op_2dup(stack)
|
115
|
+
require_stack stack, 2 do
|
116
|
+
i = stack[-1]
|
117
|
+
j = stack[-2]
|
118
|
+
stack.push(i, j)
|
119
|
+
stack
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def op_3dup(stack)
|
124
|
+
require_stack stack, 3 do
|
125
|
+
i = stack[-1]
|
126
|
+
j = stack[-2]
|
127
|
+
k = stack[-3]
|
128
|
+
stack.push(i, j, k)
|
129
|
+
stack
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def op_2over(stack)
|
134
|
+
require_stack stack, 4 do
|
135
|
+
i = stack[-3]
|
136
|
+
j = stack[-4]
|
137
|
+
stack.push(i, j)
|
138
|
+
stack
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def op_2rot(stack)
|
143
|
+
require_stack stack, 6 do
|
144
|
+
i = stack.slize!(-6,2)
|
145
|
+
stack.push(i).flatten!
|
146
|
+
stack
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def op_2swap(stack)
|
151
|
+
require_stack stack, 4 do
|
152
|
+
i = stack.pop(2)
|
153
|
+
j = stack.pop(2)
|
154
|
+
stack.push(i, j)
|
155
|
+
stack
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def op_ifdup(stack)
|
160
|
+
require_stack stack do
|
161
|
+
stack.push(stack.last) if stack.last != 0.to_openssl_bn_int
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def op_depth(stack)
|
166
|
+
stack.push(stack.size.to_openssl_bn)
|
167
|
+
stack
|
168
|
+
end
|
169
|
+
|
170
|
+
def op_nop
|
171
|
+
# Doesn't do anything
|
172
|
+
true
|
173
|
+
end
|
174
|
+
|
175
|
+
def op_drop(stack)
|
176
|
+
require_stack stack do
|
177
|
+
stack.pop
|
178
|
+
stack
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def op_dup(stack)
|
183
|
+
require_stack stack do
|
184
|
+
stack.push(stack.last)
|
185
|
+
stack
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def op_nip(stack)
|
190
|
+
require_stack stack, 2 do
|
191
|
+
stack.slice!(-2)
|
192
|
+
stack
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def op_over(stack)
|
197
|
+
require_stack stack, 2 do
|
198
|
+
stack.push(stack.slice(-2))
|
199
|
+
stack
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def op_pick_or_roll(stack, opcode)
|
204
|
+
require_stack stack, 2 do
|
205
|
+
p = stack.pop
|
206
|
+
i = stack.slice(p, 1) if opcode.hex == OP_PICK
|
207
|
+
i = stack.slice!(p, 1) if opcode.hex == OP_ROLL
|
208
|
+
stack.push(i)
|
209
|
+
stack
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def op_rot(stack)
|
214
|
+
require_stack stack, 3 do
|
215
|
+
i = stack.slice!(-3,1)
|
216
|
+
stack.push(i)
|
217
|
+
stack
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def op_swap(stack)
|
222
|
+
require_stack stack, 2 do
|
223
|
+
i = stack.slice!(-2,1)
|
224
|
+
stack.push(i)
|
225
|
+
stack
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def op_tuck(stack)
|
230
|
+
require_stack stack, 2 do
|
231
|
+
stack.insert(-3, stack.last)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Pushes the size of the item at the top of the stack
|
236
|
+
# Note: the stack processing is done in binary, and Ruby treats
|
237
|
+
# binary as strings
|
238
|
+
def op_size(stack)
|
239
|
+
stack.push([stack.last.bytesize].pack('C*'))
|
240
|
+
stack
|
241
|
+
end
|
242
|
+
|
243
|
+
# Reference client treats OP_RETURN as fail condition
|
244
|
+
# for script. OP_RETURN sets the transaction as invalid
|
245
|
+
# but otherwise the script can be valid
|
246
|
+
def op_return
|
247
|
+
false
|
248
|
+
end
|
249
|
+
|
250
|
+
def op_verify(stack, transaction_valid)
|
251
|
+
require_stack stack do
|
252
|
+
i = stack.pop
|
253
|
+
if i.to_bn.to_i == 0
|
254
|
+
stack.push(i)
|
255
|
+
transaction_valid = false
|
256
|
+
end
|
257
|
+
[stack, transaction_valid]
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def op_equal(stack)
|
262
|
+
require_stack stack, 2 do
|
263
|
+
i, j = stack.pop(2)
|
264
|
+
stack.push(i == j ? 1 : 0)
|
265
|
+
stack
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def single_stack_arithmetic(stack, opcode)
|
270
|
+
require_stack stack do
|
271
|
+
i = stack.pop.to_openssl_bn_int
|
272
|
+
case opcode.hex
|
273
|
+
when OP_1ADD
|
274
|
+
i += 1
|
275
|
+
when OP_1SUB
|
276
|
+
i -= 1
|
277
|
+
when OP_NEGATE
|
278
|
+
i = -i
|
279
|
+
when OP_ABS
|
280
|
+
i = i.abs
|
281
|
+
when OP_NOT
|
282
|
+
i = (i == 0)
|
283
|
+
when OP_0NOTEQUAL
|
284
|
+
i = (i != 0)
|
285
|
+
end
|
286
|
+
stack.push(i.to_openssl_bn)
|
287
|
+
stack
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def double_stack_arithmetic(stack)
|
292
|
+
require_stack stack, 2 do
|
293
|
+
i, j = stack.pop.map { |e| e.to_openssl_bn_int }
|
294
|
+
case opcode.hex
|
295
|
+
when OP_ADD
|
296
|
+
k = i + j
|
297
|
+
when OP_SUB
|
298
|
+
k = i - j
|
299
|
+
when OP_BOOLAND
|
300
|
+
k = (i != 0 and j != 0)
|
301
|
+
when OP_BOOLOR
|
302
|
+
k = (i != 0 or j != 0)
|
303
|
+
when OP_NUMEQUAL, OP_NUMEQUALVERIFY
|
304
|
+
k = (i == j)
|
305
|
+
op_verify if @opcode == OP_NUMEQUALVERIFY
|
306
|
+
when OP_NUMNOTEQUAL
|
307
|
+
k = (i != j)
|
308
|
+
when OP_LESSTHAN
|
309
|
+
k = (i < j)
|
310
|
+
when OP_GREATERTHAN
|
311
|
+
k = (i > j)
|
312
|
+
when OP_LESSTHANOREQUAL
|
313
|
+
k = (i <= j)
|
314
|
+
when OP_GREATERTHANOREQUAL
|
315
|
+
k = (i >= j)
|
316
|
+
when OP_MIN
|
317
|
+
k = (i < j ? i : j)
|
318
|
+
when OP_MAX
|
319
|
+
k = (i < j ? j : i)
|
320
|
+
end
|
321
|
+
stack.push(k.to_openssl_bn)
|
322
|
+
stack
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def op_within(stack)
|
327
|
+
require_stack stack do
|
328
|
+
i, j, k = stack.pop(3).map{|e| e.to_openssl_bn_int}
|
329
|
+
l = (j <= i and i < k)
|
330
|
+
stack.push(l.to_openssl_bn)
|
331
|
+
stack
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def digest(stack, opcode)
|
336
|
+
require_stack stack do
|
337
|
+
i = stack.pop
|
338
|
+
case opcode.hex
|
339
|
+
when OP_RIPEMD160
|
340
|
+
j = Digest::RMD160.digest(i)
|
341
|
+
when OP_SHA256
|
342
|
+
j = Digest::SHA256.digest(i)
|
343
|
+
when OP_SHA1
|
344
|
+
j = Digest::SHA1.digest(i)
|
345
|
+
when OP_HASH160
|
346
|
+
j = Digest::RMD160.digest(Digest::SHA256.digest(i))
|
347
|
+
when OP_HASH256
|
348
|
+
j = Digest::SHA256.digest(Digest::SHA256.digest(i))
|
349
|
+
end
|
350
|
+
stack.push(j)
|
351
|
+
stack
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def op_codeseparator(seek)
|
356
|
+
seek # Allow for future changes to this opcode
|
357
|
+
end
|
358
|
+
|
359
|
+
# One of the two opcodes that requires outside knowledge from
|
360
|
+
# the stack itself, since the transaction information isn't
|
361
|
+
# directly part of the script
|
362
|
+
def op_checksig(stack, script, code_separator, transaction)
|
363
|
+
pubkey = stack.pop
|
364
|
+
sig = stack.pop
|
365
|
+
|
366
|
+
hash_type = sig[-1].unpack('C')[0]
|
367
|
+
signature = sig[0..-2]
|
368
|
+
|
369
|
+
if is_canonical_pubkey?(pubkey) and is_canonical_signature?(signature)
|
370
|
+
stack = check_sig(signature, hash_type, pubkey, script, code_separator, transaction)
|
371
|
+
return stack.push(0.to_openssl_bn) if !stack
|
372
|
+
stack.push(1.to_openssl_bn)
|
373
|
+
else
|
374
|
+
stack.push(0.to_openssl_bn)
|
375
|
+
end
|
376
|
+
stack
|
377
|
+
end
|
378
|
+
|
379
|
+
def op_multisig(stack, script, code_separator, transaction, opcode_count)
|
380
|
+
require_stack do
|
381
|
+
i = stack.pop.to_openssl_bn_int
|
382
|
+
return false if !i.between?(1,19) #inclusive
|
383
|
+
opcode_count += i
|
384
|
+
return false if opcode_count > 201
|
385
|
+
return false if stack.size < i+1
|
386
|
+
j = stack.size
|
387
|
+
while stack.size > j-i
|
388
|
+
stack = op_checksig(stack, script, code_separator, transaction)
|
389
|
+
return stack.push(0.to_openssl_bn) if !stack
|
390
|
+
end
|
391
|
+
stack.push(1.to_openssl_bn)
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
private
|
396
|
+
|
397
|
+
def check_sig(signature, hash_type, pubkey, script, code_separator, transaction)
|
398
|
+
# get the script from codeseparator to end as subscript
|
399
|
+
# remove codeseparator from resulting script
|
400
|
+
# remove signature from script
|
401
|
+
subscript = Cryptocoin::Script.new(script[code_separator..-1])
|
402
|
+
subscript.is_subscript! # calls for removal of op_separate and signature
|
403
|
+
|
404
|
+
tx_copy = transaction.copy # in case we need to reset tx_copy
|
405
|
+
tx_copy.inputs.each do |i|
|
406
|
+
if i.index == @input_index
|
407
|
+
i.script_sig_length = Cryptocoin::Protocol::VarLenInt.from_int(subscript.raw.bytesize)
|
408
|
+
i.script_sig = subscript.raw
|
409
|
+
else
|
410
|
+
i.script_sig_length = 0.to_openssl_bn
|
411
|
+
i.script_sig = 0.to_openssl_bn
|
412
|
+
end
|
413
|
+
end
|
414
|
+
case hash_type
|
415
|
+
when 2
|
416
|
+
# SIGHASH_NONE
|
417
|
+
tx_copy.out_length = Cryptocoin::Protocol::VarLenInt.from_int(0)
|
418
|
+
tx_copy.outputs = ''
|
419
|
+
tx_copy.inputs.each do |i|
|
420
|
+
i.sequence = [0].pack('V') unless i == @input_index
|
421
|
+
end
|
422
|
+
when 3
|
423
|
+
# SIGHASH_SINGLE
|
424
|
+
if input_index >= tx_copy.outputs.length
|
425
|
+
# Special case, see https://github.com/bitcoinj/bitcoinj/blob/4df728a7d9210dc9ac5a5ae5188c89f5e9d41852/core/src/main/java/com/google/bitcoin/core/Transaction.java#L1018
|
426
|
+
# We're essentially resetting the transaction due to a bug
|
427
|
+
tx_copy = transaction
|
428
|
+
hash = Digest::SHA256([0100000000000000000000000000000000000000000000000000000000000000].pack('C*'))
|
429
|
+
bug = true
|
430
|
+
else
|
431
|
+
n_o = []
|
432
|
+
tx_copy.outputs.each do |o|
|
433
|
+
if o.index == @input_index
|
434
|
+
n_o.push(i)
|
435
|
+
elsif o.index < @input_index
|
436
|
+
# TODO: Implement this in a transaction builder
|
437
|
+
n_o.push(Cryptocoin::Structure::Transaction::Output.new(0, [-1].pack('q') + Cryptocoin::Protocol::VarLenInt.from_int(0)))
|
438
|
+
end
|
439
|
+
end
|
440
|
+
tx_copy.outputs = n_o
|
441
|
+
tx_copy.inputs.each do |i|
|
442
|
+
i.sequence = 0.to_openssl_bn_int
|
443
|
+
end
|
444
|
+
end
|
445
|
+
when 80
|
446
|
+
# SIGHASH_ANYONECANPAY
|
447
|
+
n_i = []
|
448
|
+
tx_copy.inputs.each do |i|
|
449
|
+
n_i.push(i) if i.index == @input.index
|
450
|
+
end
|
451
|
+
end
|
452
|
+
if !bug
|
453
|
+
# Do this if there's a bug in the reference client *SIGHASH_SINGLE*
|
454
|
+
i = tx_copy.raw + [hash_type].pack('V')
|
455
|
+
hash = Digest::SHA256.digest(Digest::SHA256.digest(i))
|
456
|
+
end
|
457
|
+
begin
|
458
|
+
k = OpenSSL::PKey::EC.new("secp256k1")
|
459
|
+
k.public_key = OpenSSL::PKey::EC::Point.new(k.group, OpenSSL::BN.new(pubkey.unpack('H*')[0], 16))
|
460
|
+
if k.dsa_verify_asn1(hash, signature)
|
461
|
+
stack.push(1.to_openssl_bn)
|
462
|
+
else
|
463
|
+
stack.push(0.to_openssl_bn)
|
464
|
+
end
|
465
|
+
rescue OpenSSL::PKey::ECError, OpenSSL::PKey::EC::Point::Error
|
466
|
+
stack.push(0.to_openssl_bn)
|
467
|
+
end
|
468
|
+
stack
|
469
|
+
end
|
470
|
+
|
471
|
+
def is_canonical_signature?(sig)
|
472
|
+
return false if sig.bytesize < 9
|
473
|
+
return false if sig.bytesize > 73
|
474
|
+
return false if !["\x01", "\x02", "\x03", "\x80"].include?(sig[-1])
|
475
|
+
return false if sig[0] != "\x30"
|
476
|
+
return false if sig[1].to_bn_to_i != sig.bytesize-3
|
477
|
+
r_length = sig[3].to_bn.to_i
|
478
|
+
return false if 5 + r_length >= sig.bytesize
|
479
|
+
s_length = sig[5+r_length]
|
480
|
+
return false if r_length + s_length + 7 != sig.bytesize
|
481
|
+
r = sig[4, r_length]
|
482
|
+
return false if r[-2] != "\x02"
|
483
|
+
return false if r.bytesize == 0
|
484
|
+
return false if r.bytes.inject{|x,y| (x<<8) | y} & 0x80
|
485
|
+
return false if r.bytesize > 1 and (r[0] == "\x00" and !(r[1].getbyte(0) & 0x80))
|
486
|
+
true
|
487
|
+
end
|
488
|
+
|
489
|
+
def is_canonical_pubkey?(pubkey)
|
490
|
+
return false if pubkey.bytesize < 33
|
491
|
+
case pubkey[0]
|
492
|
+
when "\x04"
|
493
|
+
# uncompressed pubkey
|
494
|
+
return false if pubkey.bytesize != 65
|
495
|
+
when "\x02", "\x03"
|
496
|
+
# compressed pubkey
|
497
|
+
return false if pubkey.bytesize != 33
|
498
|
+
else
|
499
|
+
# invalid first byte
|
500
|
+
return false
|
501
|
+
end
|
502
|
+
true
|
503
|
+
end
|
504
|
+
|
505
|
+
def require_stack(stack, size=1, &block)
|
506
|
+
if stack.size >= size
|
507
|
+
yield
|
508
|
+
else
|
509
|
+
nil
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
515
|
+
end
|