-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathopfns.py
332 lines (262 loc) · 9.19 KB
/
opfns.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
import copy
import hashlib
import ecdsa
import math
import btct
import bytestream
import script
class InvalidTransactionException(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return repr(self.msg)
###### Flow Control #########
def nop(stream, machine):
pass
# op function implementations
def empty_array(stream, machine):
empty_array = bytestream("")
stream.push(empty_array)
return empty_array
def pusher_maker(i):
def pusher(stream, machine):
data = stream.read(i)
machine.push(data)
return data
return pusher
def reader_pusher_maker(i):
def reader_pusher(stream, machine):
nbytes = stream.read(i).unsigned()
data = stream.read(nbytes)
machine.push(data)
return data
return reader_pusher
def numpusher_maker(x):
def numpusher(stream, machine):
data = bytestream.fromunsigned(x)
machine.push(data)
return data
return numpusher
def toalt(stream, machine):
data = machine.stack.pop()
machine.alt.push(data)
return data
def fromalt(stream, machine):
data = machine.alt.pop()
machine.stack.push(data)
return data
def ifdup(stream, machine):
data = machine.stack.pop()
if data.signed() != 0:
machine.stack.push(copy.deepcopy(data))
machine.stack.push(data)
return data
def depth(stream, machine):
data = len(machine.stack)
machine.stack.push(data)
return data
def drop(stream, machine):
machine.stack.pop()
# should return data and copied data
def dup(stream, machine):
data = machine.stack.pop()
machine.push(copy.deepcopy(data))
machine.push(data)
return data
def nip(stream, machine):
data = machine.stack.pop(-2)
return data
def over(stream, machine):
data = copy.deepcopy(machine.stack[-2])
machine.stack.push(data)
return data
def pick(stream, machine):
n = stream.read(1).unsigned()
data = copy.deepcopy(machine.stack[-n])
machine.stack.push(data)
return data
def roll(stream, machine):
n = stream.read(1).unsigned()
data = machine.stack.pop(-n)
machine.stack.push(data)
return data
def rot(stream, machine):
data = machine.stack.pop(-3)
machine.stack.push(data)
return data
def swap(stream, machine):
data = machine.stack.pop(-2)
machine.stack.push(data)
return data
def tuck(stream, machine):
top = machine.stack.pop()
middle = machine.stack.pop()
data = copy.deepcopy(top)
machine.stack.push(data)
machine.stack.push(middle)
machine.stack.push(top)
return data
def drop2(stream, machine):
[machine.stack.pop() for i in range(2)]
def dup2(stream, machine):
i2 = copy.deepcopy(machine.stack[-2])
i1 = copy.deepcopy(machine.stack[-1])
machine.stack.push(i2)
machine.stack.push(i1)
return i1
def dup3(stream, machine):
i3 = copy.deepcopy(machine.stack[-3])
i2 = copy.deepcopy(machine.stack[-2])
i1 = copy.deepcopy(machine.stack[-1])
machine.stack.push(i3)
machine.stack.push(i2)
machine.stack.push(i1)
return i1
def over2(stream, machine):
i1 = copy.deepcopy(machine.peek[-4])
i2 = copy.deepcopy((machine.peek[-3]))
machine.push(i1)
machine.push(i2)
def rot2(stream, machine):
i1 = machine.pop(-6)
i2 = machine.pop(-5)
machine.push(i1)
machine.push(i2)
def swap2(stream, machine):
i1 = machine.pop(-4)
i2 = machine.pop(-3)
machine.push(i1)
machine.push(i2)
#TODO please double check (tian)
def opt_size(stream, machine):
top_element = machine.peek() # hex value
if top_element[2:] == '\\x':
hex_value = top_element[2:] # get ride of \x
else:
hex_value = top_element
string_top_element = ''.join([chr(int(s, 16))
for s in [hex_value[i: i+2] for i in range(0, len(hex_value), 2)]])
size_top = len(string_top_element)
machine.push(bytestream.bytestream(hex(size_top))) # push hex representation of size_top
######## Arithmetic ops #############
def op_add1(stream, machine):
top_element = machine.pop()
if top_element[2:] == '\\x':
hex_value = top_element[2:] # get ride of \x
else:
hex_value = top_element
top_stream = bytestream.bytestream(hex_value)
if len(top_stream) > 4: # input is longer than 4 bytes
raise InvalidTransactionException('Integer longer than 4 bytes')
machine.push(bytestream.bytestream(hex(top_stream.unsigned()+1))) #TODO verify little or big
def hash160(stream, machine):
data = machine.pop()
sha256 = hashlib.new('sha256')
ripemd160 = hashlib.new('ripemd160')
sha256.update(data.stream.decode('hex'))
ripemd160.update(sha256.digest())
hashed = bytestream.bytestream(ripemd160.hexdigest())
machine.push(hashed)
return bytestream.bytestream(ripemd160.hexdigest())
def equal(stream, machine):
x1 = machine.pop()
x2 = machine.pop()
if x1 == x2:
machine.push(bytestream.fromunsigned(1))
else:
machine.push(bytestream.fromunsigned(0))
def ret(stream, machine):
raise InvalidTransactionException("OP_RETURN evaluated")
def verifier_maker(f, msg):
def verifier(stream, machine):
f(stream, machine)
top = machine.peek().unsigned()
if top == 0:
raise InvalidTransactionException(msg)
else:
machine.pop()
return verifier
def disabled_maker(msg):
def disabled(stream, machine):
raise InvalidTransactionException(msg)
return disabled
def unary_arith_maker(f):
def unary_arith(stream, machine):
s = machine.pop()
l = len(s)
x = s.signed(endian="little")
machine.push(bytestream.fromsigned(f(x), l))
return unary_arith
def binary_arith_maker(f):
def binary_arith(stream, machine):
s1 = machine.pop()
l1 = len(s1)
x1 = s1.signed(endian="little")
s2 = machine.pop()
l2 = len(s2)
x2 = s2.signed(endian="little")
machine.push(bytestream.fromsigned(f(x1,x2), max(l1,l2)))
return binary_arith
def opnot(stream, machine):
x = machine.pop()
if x.signed() == 0:
machine.push(bytestream.fromunsigned(1,len(x)))
elif x.signed() == 1:
machine.push(bytestream.fromunsigned(0,len(x)))
else:
machine.push(bytestream.fromunsigned(0,1))
def op0ne(stream, machine):
x = machine.pop()
if x.signed() == 0:
machine.push(bytestream.fromunsigned(0,len(x)))
else:
machine.push(bytestream.fromunsigned(1,1))
# this breaks the usual interface; added a special case to the
# interpreter
def checksig(stream, machine, transaction, index, subscript):
"""
For details, please see https://en.bitcoin.it/wiki/OP_CHECKSIG.
"""
# How it works
# Firstly always this (the default) procedure is
# applied: Signature verification process of the default procedure
# the public key and the signature are popped from the stack, in
# that order. If the hash-type value is 0, then it is replaced by
# the last_byte of the signature. Then the last byte of the
# signature is always deleted.
pubkey = machine.pop()
sig = machine.pop()
#sig.stream = sig.stream[:-2] # this is actually done later
# A new subscript is created from the instruction from the most
# recently parsed OP_CODESEPARATOR (last one in script) to the end
# of the script. If there is no OP_CODESEPARATOR the entire script
# becomes the subscript (hereby referred to as subScript)
# not implemented yet (james)
# The sig is deleted from subScript.
# note: this is nonstandard so I am ignoring it (james)
# All OP_CODESEPARATORS are removed from subScript
# not implemented yet (james)
# The hashtype is removed from the last byte of the sig and stored (as 4 bytes)
hashtype = bytestream.fromunsigned(bytestream.bytestream(sig.stream[-2:]).unsigned(),4)
sig.stream = sig.stream[:-2]
# A copy is made of the current transaction (hereby referred to txCopy)
txCopy = copy.deepcopy(transaction)
# The scripts for all transaction inputs in txCopy are set to empty scripts (exactly 1 byte 0x00)
for i in xrange(txCopy.tx_in_count):
txCopy.tx_in[i].script_length = 0
txCopy.tx_in[i].script = script.script(bytestream.fromunsigned(0,1))
# The script for the current transaction input in txCopy is set to subScript (lead in by its length as a var-integer encoded!)
txCopy.tx_in[index-1].script_length = len(subscript)
txCopy.tx_in[index-1].script = subscript
# Serialize txCopy and append hashtype
serial = txCopy.encode() + hashtype
# hash twice with sha256
msg = ((hashlib.sha256(hashlib.sha256(serial.stream.decode('hex')).digest()).digest()))# [::-1]).encode('hex_codec')
# verify via ecdsa
key = btct.decompress(pubkey.stream)
vk = ecdsa.VerifyingKey.from_string(key[2:].decode('hex'), curve=ecdsa.SECP256k1)
try:
vk.verify_digest(sig.stream.decode('hex'), msg, sigdecode=ecdsa.util.sigdecode_der)
machine.push(bytestream.fromunsigned(1,1))
except ecdsa.BadSignatureError:
machine.push(bytestream.fromunsigned(0,1))