|
|
@@ -0,0 +1,1662 @@
|
|
|
+#! /usr/bin/env/python3
|
|
|
+import hashlib
|
|
|
+import logging
|
|
|
+from .crypto import *
|
|
|
+from binascii import hexlify, unhexlify
|
|
|
+from datetime import datetime
|
|
|
+from .transaction import *
|
|
|
+
|
|
|
+global transaction
|
|
|
+global prog_stack
|
|
|
+global validity
|
|
|
+global control_flow_stack
|
|
|
+global alt_stack
|
|
|
+
|
|
|
+#The actual interpreter for our scriptlanguage
|
|
|
+def interpreter(inputscript: str, outputscript: str, tx):
|
|
|
+
|
|
|
+ global transaction
|
|
|
+ #get nLockTime from transaction for OP_CHECKLOCKTIME / OP_CHECKLOCKTIMEVERIFY / OP_NOP2
|
|
|
+ transaction = tx
|
|
|
+
|
|
|
+ #parse script-strings to LabScript
|
|
|
+ scriptSig = LabScript(inputscript + " " + outputscript)
|
|
|
+ #scriptPubKey = LabScript(outputscript)
|
|
|
+
|
|
|
+ #create Scriptstack
|
|
|
+ stack = LabStack()
|
|
|
+
|
|
|
+ #execute scripts and merge the results
|
|
|
+ return scriptSig.execute_script(stack)
|
|
|
+ #return scriptPubKey.execute_script(stack)
|
|
|
+
|
|
|
+def invalidate():
|
|
|
+ """
|
|
|
+ Makes the script invalid.
|
|
|
+ """
|
|
|
+ global validity
|
|
|
+ validity = False
|
|
|
+
|
|
|
+class LabStack:
|
|
|
+ """
|
|
|
+ Labscript uses a stack for imperative control flow. LabStack is a simple
|
|
|
+ implementation of a stack. The LabStack is used by the LabScript
|
|
|
+ interpreter to store intermediate values. A newly initialized LabStack is
|
|
|
+ always empty.
|
|
|
+ """
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ self.items = []
|
|
|
+ self.sp = 0
|
|
|
+ return
|
|
|
+
|
|
|
+ def is_empty(self):
|
|
|
+ """
|
|
|
+ Tests whether the stack is empty and returns true if yes, false
|
|
|
+ otherwise.
|
|
|
+ """
|
|
|
+ return self.sp == 0
|
|
|
+
|
|
|
+ def size(self):
|
|
|
+ """
|
|
|
+ Returns the number of items currently in the stack.
|
|
|
+ """
|
|
|
+ return self.sp
|
|
|
+
|
|
|
+ def peek(self):
|
|
|
+ """
|
|
|
+ Returns the item at the top of the stack without removing it from the
|
|
|
+ stack. If the stack is empty, None is returned.
|
|
|
+ """
|
|
|
+ if self.is_empty():
|
|
|
+ return None
|
|
|
+
|
|
|
+ return self.items[self.sp-1]
|
|
|
+
|
|
|
+ def push(self, item):
|
|
|
+ """
|
|
|
+ Appends a new item to the top of the stack and increases the stack
|
|
|
+ pointer by 1.
|
|
|
+ """
|
|
|
+ self.items.append(item)
|
|
|
+ self.sp += 1
|
|
|
+ return
|
|
|
+
|
|
|
+ def pop(self):
|
|
|
+ """
|
|
|
+ Removes the item at the top of the stack and returns it and the stack
|
|
|
+ pointer is decreased by 1. If the stack is empty, None is returned.
|
|
|
+ """
|
|
|
+ if self.is_empty():
|
|
|
+ return None
|
|
|
+
|
|
|
+ self.sp -= 1
|
|
|
+ return self.items.pop()
|
|
|
+
|
|
|
+ def set_sp(pos):
|
|
|
+ """
|
|
|
+ Sets the stack pointer to pos and alters the stack accordingly:
|
|
|
+ If pos is greater than the current sp, the stack is made bigger and
|
|
|
+ the new items are initialized with None. If pos is less than the
|
|
|
+ current sp, the stack is made smaller and all items above the current
|
|
|
+ sp are simply removed from the stack.
|
|
|
+ """
|
|
|
+ while(sp != pos):
|
|
|
+ if pos > self.sp:
|
|
|
+ self.push(None)
|
|
|
+ else:
|
|
|
+ self.pop()
|
|
|
+ return
|
|
|
+
|
|
|
+ def print_stack(self):
|
|
|
+ """
|
|
|
+ Prints all items on the stack, from top to bottom.
|
|
|
+ """
|
|
|
+ for i in self.items[::-1]:
|
|
|
+ print(str(i))
|
|
|
+ return
|
|
|
+
|
|
|
+
|
|
|
+class LabScript:
|
|
|
+ """
|
|
|
+ LabScript is a simple imperative stack-based script language. This class
|
|
|
+ implements a script as a List of commands to be executed from left to
|
|
|
+ right. The items in the list of commands can be any data or an operation.
|
|
|
+
|
|
|
+ USAGE:
|
|
|
+ The constructor is called using a string that represents the script.
|
|
|
+ This string is a long chain consisting of commands, which are arbitrary
|
|
|
+ substrings, each separated by a whitespace. If a command substring matches
|
|
|
+ an opcode string, as specified in the OPLIST below, the interpreter will
|
|
|
+ parse opcode into an operation and execute its behavior. Any other command
|
|
|
+ will be simply pushed onto the stack as data.
|
|
|
+ """
|
|
|
+
|
|
|
+ """
|
|
|
+ Following is an overview of all implemented operations sorted after area
|
|
|
+ of application.
|
|
|
+ For more information go to https://en.bitcoin.it/wiki/Script
|
|
|
+ or read the explanation within the op-implementation below:
|
|
|
+
|
|
|
+ Constants:
|
|
|
+
|
|
|
+ OP_0
|
|
|
+ OP_FALSE
|
|
|
+ OP_1NEGATE
|
|
|
+ OP_1
|
|
|
+ OP_TRUE
|
|
|
+
|
|
|
+ Flow Control:
|
|
|
+
|
|
|
+ OP_NOP
|
|
|
+ OP_IF
|
|
|
+ OP_NOTIF
|
|
|
+ OP_ELSE
|
|
|
+ OP_ENDIF
|
|
|
+ OP_VERIFY
|
|
|
+ OP_RETURN
|
|
|
+
|
|
|
+ Stack:
|
|
|
+
|
|
|
+ OP_TOALTSTACK
|
|
|
+ OP_FROMALTSTACK
|
|
|
+ OP_IFDUP
|
|
|
+ OP_DROP
|
|
|
+ OP_DUP
|
|
|
+ OP_NIP
|
|
|
+ OP_OVER
|
|
|
+ OP_PICK
|
|
|
+ OP_ROLL
|
|
|
+ OP_ROT
|
|
|
+ OP_SWAP
|
|
|
+ OP_TUCK
|
|
|
+ OP_2DROP
|
|
|
+ OP_2DUP
|
|
|
+ OP_3DUP
|
|
|
+ OP_2OVER
|
|
|
+ OP_2ROT
|
|
|
+ OP_2SWAP
|
|
|
+
|
|
|
+ Splice:
|
|
|
+
|
|
|
+ -
|
|
|
+
|
|
|
+ Bitwise logic:
|
|
|
+
|
|
|
+ OP_INVERT
|
|
|
+ OP_AND
|
|
|
+ OP_OR
|
|
|
+ OP_XOR
|
|
|
+ OP_EQUAL
|
|
|
+ OP_EQUALVERIFY
|
|
|
+
|
|
|
+ Arithmetic:
|
|
|
+
|
|
|
+ OP_1ADD
|
|
|
+ OP_1SUB
|
|
|
+ OP_2MUL
|
|
|
+ OP_2DIV
|
|
|
+ OP_NOT
|
|
|
+ OP_0NOTEQUAL
|
|
|
+ OP_ADD
|
|
|
+ OP_SUB
|
|
|
+ OP_MUL
|
|
|
+ OP_DIV
|
|
|
+ OP_MOD
|
|
|
+ OP_BOOLAND
|
|
|
+ OP_BOOLOR
|
|
|
+ OP_NUMEQUAL
|
|
|
+ OP_NUMEQUALVERIFY
|
|
|
+ OP_NUMNOTEQUAL
|
|
|
+ OP_LESSTHAN
|
|
|
+ OP_GREATERTHAN
|
|
|
+ OP_LESSTHANOREQUAL
|
|
|
+ OP_GREATERTHANOREQUAL
|
|
|
+ OP_MIN
|
|
|
+ OP_MAX
|
|
|
+ OP_WITHIN
|
|
|
+
|
|
|
+ Crypto:
|
|
|
+
|
|
|
+ OP_RIPDEM160
|
|
|
+ OP_SHA1
|
|
|
+ OP_SHA256
|
|
|
+ OP_HASH160
|
|
|
+ OP_HASH256
|
|
|
+ OP_CHECKSIG
|
|
|
+ OP_CHECKSIGVERIFY
|
|
|
+ #OP_CHECKMULTISIG ???
|
|
|
+ #OP_CHECKMULTISIGVERIFY ???
|
|
|
+
|
|
|
+ Locktime:
|
|
|
+
|
|
|
+ OP_CHECKLOCKTIME
|
|
|
+ OP_CHECKLOCKTIMEVERIFY
|
|
|
+ OP_NOP2
|
|
|
+
|
|
|
+ Reserved words:
|
|
|
+
|
|
|
+ OP_NOP1
|
|
|
+ OP_NOP4
|
|
|
+ OP_NOP5
|
|
|
+ OP_NOP6
|
|
|
+ OP_NOP7
|
|
|
+ OP_NOP8
|
|
|
+ OP_NOP9
|
|
|
+ OP_NOP10
|
|
|
+ """
|
|
|
+
|
|
|
+ def __init__(self, list_of_commands: str):
|
|
|
+ self.prog_queue = list_of_commands.split()
|
|
|
+ self.pc = 1 # program counter
|
|
|
+ self.pend = len(self.prog_queue) # end of program
|
|
|
+
|
|
|
+ self.valid = True # is the script valid? if not, better not use it
|
|
|
+ # for anything important.
|
|
|
+
|
|
|
+ self.if_endif_syntax_check()
|
|
|
+ return
|
|
|
+
|
|
|
+
|
|
|
+ def to_string(self):
|
|
|
+ schtring_aeeh_string = ""
|
|
|
+ for i in self.prog_queue:
|
|
|
+ schtring_aeeh_string+=str(i)+" "
|
|
|
+
|
|
|
+ schtring_aeeh_string = schtring_aeeh_string[:-1]
|
|
|
+ return schtring_aeeh_string
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ # operation implementations
|
|
|
+
|
|
|
+ def op_ripemd160():
|
|
|
+ #The input is hashed using RIPEMD160.
|
|
|
+ #DONE
|
|
|
+
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ ripemd160 = hashlib.new('ripemd160')
|
|
|
+ ripemd160.update(str(prog_stack.pop()).encode('utf-8'))
|
|
|
+ ripemd160 = hexlify(ripemd160.digest())
|
|
|
+ prog_stack.push(ripemd160.decode('utf-8'))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_sha1():
|
|
|
+ #The input is hashed using SHA-1.
|
|
|
+ #DONE
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ sha1 = hashlib.sha1()
|
|
|
+ sha1.update(str(prog_stack.pop()).encode('utf-8'))
|
|
|
+ sha1 = hexlify(sha1.digest())
|
|
|
+ prog_stack.push(sha1.decode('utf-8'))
|
|
|
+ return
|
|
|
+ def op_sha256():
|
|
|
+ #The input is hashed using SHA-256.
|
|
|
+ #DONE
|
|
|
+
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ sha256 = hashlib.sha256()
|
|
|
+ sha256.update(str(prog_stack.pop()).encode('utf-8'))
|
|
|
+ sha256 = hexlify(sha256.digest())
|
|
|
+ prog_stack.push(sha256.decode('utf-8'))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_hash256():
|
|
|
+ #The input is hashed two times with SHA-256.
|
|
|
+ #DONE
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ sha256 = hashlib.sha256()
|
|
|
+ sha256.update(str(prog_stack.pop()).encode('utf-8'))
|
|
|
+ sha256 = hexlify(sha256.digest())
|
|
|
+ prog_stack.push(sha256.decode('utf-8'))
|
|
|
+
|
|
|
+ sha256 = hashlib.sha256()
|
|
|
+ sha256.update(str(prog_stack.pop()).encode('utf-8'))
|
|
|
+ sha256 = hexlify(sha256.digest())
|
|
|
+ prog_stack.push(sha256.decode('utf-8'))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_hash160():
|
|
|
+ #The input is hashed twice: first with SHA-256 and then with
|
|
|
+ # RIPEMD-160.
|
|
|
+ #DONE
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ sha256 = hashlib.sha256()
|
|
|
+ sha256.update(str(prog_stack.pop()).encode('utf-8'))
|
|
|
+ sha256 = hexlify(sha256.digest())
|
|
|
+ prog_stack.push(sha256.decode('utf-8'))
|
|
|
+
|
|
|
+ ripemd160 = hashlib.new('ripemd160')
|
|
|
+ ripemd160.update(str(prog_stack.pop()).encode('utf-8'))
|
|
|
+ ripemd160 = hexlify(ripemd160.digest())
|
|
|
+ prog_stack.push(ripemd160.decode('utf-8'))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_checksig():
|
|
|
+ # The signature used by OP_CHECKSIG must be a valid signature for
|
|
|
+ # this hash and public key.
|
|
|
+ #If it is, 1 is returned, 0 otherwise.
|
|
|
+
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ pubKey = Key.from_json_compatible(prog_stack.pop())
|
|
|
+
|
|
|
+ tx_hash = transaction.get_hash()
|
|
|
+
|
|
|
+ sig = unhexlify(prog_stack.pop())
|
|
|
+
|
|
|
+ if pubKey.verify_sign(tx_hash, sig):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ logging.warning("Signature not verified")
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_checksigverify():
|
|
|
+ #runs checksig and verify afterwards
|
|
|
+
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ mystring = "-----BEGIN PUBLIC KEY-----" + prog_stack.pop() + "-----END PUBLIC KEY-----"
|
|
|
+ pubKey = bytes(mystring, 'utf-8')
|
|
|
+ sig = unhexlify(prog_stack.pop())
|
|
|
+ #sig = bytes(prog_stack.pop(), 'utf-8')
|
|
|
+
|
|
|
+ signing_service = Key(pubKey)
|
|
|
+
|
|
|
+ hash = transaction.get_hash()
|
|
|
+
|
|
|
+ #verify signature
|
|
|
+ if signing_service.verify_sign(hash, sig):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ logging.warning("Signature not verified")
|
|
|
+ prog_stack.push(str(0))
|
|
|
+
|
|
|
+ #verify
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ if(not(int(prog_stack.pop()))):
|
|
|
+ logging.warning('Transaction not valid!')
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+
|
|
|
+ def op_nop():
|
|
|
+ #Does nothing
|
|
|
+ #DONE
|
|
|
+ pass
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_equal():
|
|
|
+ #Returns 1 if the inputs are exactly equal, 0 otherwise.
|
|
|
+ #DONE
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = prog_stack.pop()
|
|
|
+ x2 = prog_stack.pop()
|
|
|
+
|
|
|
+ if(x1 == x2):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ else:
|
|
|
+ prog_stack.push(str(0))
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_verify():
|
|
|
+ #Marks transaction as invalid if top stack value is not true. The
|
|
|
+ # top stack value is removed.
|
|
|
+ #DONE
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ if(not(int(prog_stack.pop()))):
|
|
|
+ logging.warning('Transaction not valid!')
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_if():
|
|
|
+ #DONE
|
|
|
+ # If the top stack value is not False, the statements are executed.
|
|
|
+ # The top stack value is removed.
|
|
|
+
|
|
|
+ # this operation pushes a new conditional flag onto the
|
|
|
+ # control flow stack according to this scheme:
|
|
|
+ #
|
|
|
+ # control_flow_stack, prog_stack: push flag
|
|
|
+ # -----------------------------------------------
|
|
|
+ # ALLOW_ALL, TRUE: push ALLOW_ALL
|
|
|
+ # ALLOW_ALL, FALSE: push ALLOW_IF_ELSE
|
|
|
+ #
|
|
|
+ # ALLOW_IF_ELSE, TRUE: push ALLOW_IF
|
|
|
+ # ALLOW_IF_ELSE, FALSE: push ALLOW_IF
|
|
|
+ #
|
|
|
+ # ALLOW_IF, TRUE: push ALLOW_IF
|
|
|
+ # ALLOW_IF, FALSE: push ALLOW_IF
|
|
|
+
|
|
|
+ ALLOW_ALL=0 # Any operation can be executed.
|
|
|
+ ALLOW_IF_ELSE=1 # Nothing but ifs, elses and endifs can be executed.
|
|
|
+ ALLOW_IF=2 # Nothing but ifs and endifs can be executed.
|
|
|
+
|
|
|
+ flag = control_flow_stack.peek()
|
|
|
+
|
|
|
+ if flag == ALLOW_ALL:
|
|
|
+
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ if int(prog_stack.pop()):
|
|
|
+ control_flow_stack.push(ALLOW_ALL)
|
|
|
+ else:
|
|
|
+ control_flow_stack.push(ALLOW_IF_ELSE)
|
|
|
+ else:
|
|
|
+ control_flow_stack.push(ALLOW_IF)
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_notif():
|
|
|
+ #DONE
|
|
|
+ # If the top stack value is False, the statements are executed.
|
|
|
+ # The top stack value is removed.
|
|
|
+
|
|
|
+ # this operation pushes a new conditional flag onto the
|
|
|
+ # control flow stack. Any operations that follow are only
|
|
|
+ # executed if the current flag is True.
|
|
|
+
|
|
|
+ ALLOW_ALL=0 # Any operation can be executed.
|
|
|
+ ALLOW_IF_ELSE=1 # Nothing but ifs, elses and endifs can be executed.
|
|
|
+ ALLOW_IF=2 # Nothing but ifs and endifs can be executed.
|
|
|
+
|
|
|
+ flag = control_flow_stack.peek()
|
|
|
+
|
|
|
+ if flag == ALLOW_ALL:
|
|
|
+
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ if int(prog_stack.pop()):
|
|
|
+ control_flow_stack.push(ALLOW_IF_ELSE)
|
|
|
+ else:
|
|
|
+ control_flow_stack.push(ALLOW_ALL)
|
|
|
+ else:
|
|
|
+ control_flow_stack.push(ALLOW_IF)
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_else():
|
|
|
+ #DONE
|
|
|
+ # If the preceding OP_IF or OP_NOTIF or OP_ELSE was not executed
|
|
|
+ # then these statements are and if the preceding OP_IF or OP_NOTIF
|
|
|
+ # or OP_ELSE was executed then these statements are not.
|
|
|
+
|
|
|
+ # this operation changes the current conditional flag onto the
|
|
|
+ # control flow stack according to this scheme:
|
|
|
+ #
|
|
|
+ # control_flow_stack, prog_stack: push flag
|
|
|
+ # -----------------------------------------------
|
|
|
+ # ALLOW_ALL -> ALLOW_IF_ELSE
|
|
|
+ # ALLOW_IF_ELSE -> ALLOW_ALL
|
|
|
+ # if the flag is ALLOW_IF, this is never executed.
|
|
|
+
|
|
|
+ ALLOW_ALL=0 # Any operation can be executed.
|
|
|
+ ALLOW_IF_ELSE=1 # Nothing but ifs, elses and endifs can be executed.
|
|
|
+ ALLOW_IF=2 # Nothing but ifs and endifs can be executed.
|
|
|
+
|
|
|
+ flag = control_flow_stack.pop()
|
|
|
+ if flag == ALLOW_ALL:
|
|
|
+ control_flow_stack.push(ALLOW_IF_ELSE)
|
|
|
+ if flag == ALLOW_IF_ELSE:
|
|
|
+ control_flow_stack.push(ALLOW_ALL)
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_endif():
|
|
|
+ #DONE
|
|
|
+ # Ends an if/else block.
|
|
|
+ control_flow_stack.pop()
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_return():
|
|
|
+
|
|
|
+ """Marks transaction as invalid.
|
|
|
+ A standard way of attaching extra data to transactions is to add a zero-value output with a
|
|
|
+ scriptPubKey consisting of OP_RETURN followed by exactly one pushdata op. Such outputs are
|
|
|
+ provably unspendable, reducing their cost to the network.
|
|
|
+ Currently it is usually considered non-standard (though valid) for a transaction to have more
|
|
|
+ than one OP_RETURN output or an OP_RETURN output with more than one pushdata op. """
|
|
|
+
|
|
|
+ #DONE
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ logging.warning('Transaction not valid!')
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_dup():
|
|
|
+ #Duplicates the top stack item.
|
|
|
+ #DONE
|
|
|
+
|
|
|
+ if(prog_stack.peek()!=None):
|
|
|
+ prog_stack.push(prog_stack.peek())
|
|
|
+ return
|
|
|
+
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_drop():
|
|
|
+ #Removes the top stack item.
|
|
|
+ #DONE
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is already empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.pop()
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_checklocktime():
|
|
|
+
|
|
|
+ global transaction
|
|
|
+
|
|
|
+ #Error Indicator
|
|
|
+ errno = 0
|
|
|
+
|
|
|
+ #Error if Stack is empty
|
|
|
+ if(prog_stack.is_empty() or prog_stack.size() < 2):
|
|
|
+ errno = 1
|
|
|
+
|
|
|
+
|
|
|
+ #if top stack item is greater than the transactions nLockTime field ERROR
|
|
|
+ temp = float(prog_stack.pop())
|
|
|
+ try:
|
|
|
+ timestamp = datetime.fromtimestamp(temp)
|
|
|
+ except TypeError:
|
|
|
+ logging.error("A timestamp needs to be supplied after the OP_CHECKLOCKTIME operation!")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ #TODO we need to make sure that the timezones are being taken care of
|
|
|
+ if(timestamp > datetime.utcnow()):
|
|
|
+ print("You need to wait at least " + str(timestamp - datetime.utcnow()) + " to spend this Tx")
|
|
|
+ errno = 3
|
|
|
+
|
|
|
+ if(errno):
|
|
|
+ #errno = 1 Stack is empty
|
|
|
+ if(errno == 1):
|
|
|
+ logging.warning('Stack is empty!')
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ #errno = 2 Top-Stack-Value < 0
|
|
|
+ if(errno == 2):
|
|
|
+ logging.warning('Top-Stack-Value < 0')
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ #errno = 3 top stack item is greater than the transactions
|
|
|
+ if(errno == 3):
|
|
|
+ #logging.warning('you need to wait more to unlock the funds!')
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_equalverify():
|
|
|
+ #Same as OP_EQUAL, but runs OP_VERIFY afterward.
|
|
|
+ #DONE
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = prog_stack.pop()
|
|
|
+ x2 = prog_stack.pop()
|
|
|
+
|
|
|
+ if(x1 == x2):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ else:
|
|
|
+ prog_stack.push(str(0))
|
|
|
+
|
|
|
+ #verify
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ if(not(int(prog_stack.pop()))):
|
|
|
+ logging.warning('Transaction not valid!')
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_invert():
|
|
|
+ #Flips all of the bits in the input. disabled at BITCOIN.
|
|
|
+ #DONE
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(~ int(prog_stack.pop())))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_and():
|
|
|
+ #Boolean and between each bit in the inputs. disabled at BITCOIN.
|
|
|
+ #DONE
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = int(prog_stack.pop())
|
|
|
+ x2 = int(prog_stack.pop())
|
|
|
+
|
|
|
+ prog_stack.push(str(x1 & x2))
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_or():
|
|
|
+ #Boolean or between each bit in the inputs. disabled at BITCOIN.
|
|
|
+ #DONE
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = int(prog_stack.pop())
|
|
|
+ x2 = int(prog_stack.pop())
|
|
|
+
|
|
|
+ prog_stack.push(str(x1 | x2))
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_xor():
|
|
|
+ #Boolean exclusive or between each bit in the inputs. disabled at BITCOIN.
|
|
|
+ #DONE
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = int(prog_stack.pop())
|
|
|
+ x2 = int(prog_stack.pop())
|
|
|
+
|
|
|
+ prog_stack.push(str(x1 ^ x2))
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_true():
|
|
|
+ #op_1()
|
|
|
+ #The number 1 is pushed onto the stack.
|
|
|
+ #DONE
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_false():
|
|
|
+ #op_0()
|
|
|
+ #The number 0 is pushed onto the stack.
|
|
|
+ #DONE
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_1negate():
|
|
|
+ #The number -1 is pushed onto the stack.
|
|
|
+ #DONE
|
|
|
+ prog_stack.push(str(-1))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_toaltstack():
|
|
|
+ #Puts the input onto the top of the alt stack.
|
|
|
+ #Removes it from the main stack.
|
|
|
+ #DONE
|
|
|
+
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+
|
|
|
+ alt_stack.push(prog_stack.pop())
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_fromaltstack():
|
|
|
+ #Puts the input onto the top of the main stack.
|
|
|
+ #Removes it from the alt stack.
|
|
|
+ #DONE
|
|
|
+
|
|
|
+ if(alt_stack.is_empty()):
|
|
|
+ logging.warning("Alt_stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(alt_stack.pop())
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_ifdup():
|
|
|
+ #If the top stack value is not 0, duplicate it.
|
|
|
+ #DONE
|
|
|
+
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ if(int(prog_stack.peek()) != 0):
|
|
|
+ op_dup()
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_nip():
|
|
|
+ #Removes the second-to-top stack item.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = prog_stack.pop()
|
|
|
+ prog_stack.pop()
|
|
|
+ prog_stack.push(x1)
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_over():
|
|
|
+ #Copies the second-to-top stack item to the top.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = prog_stack.pop()
|
|
|
+ x2 = prog_stack.peek()
|
|
|
+ prog_stack.push(x1)
|
|
|
+ prog_stack.push(x2)
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_pick():
|
|
|
+ #The item n back in the stack is copied to the top.
|
|
|
+
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ n = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(prog_stack.size() < n):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ stack_list = list()
|
|
|
+
|
|
|
+ for i in range(n):
|
|
|
+ stack_list.append(prog_stack.pop())
|
|
|
+
|
|
|
+ for i in range(n):
|
|
|
+ prog_stack.push(stack_list[n - 1 - i])
|
|
|
+
|
|
|
+ prog_stack.push(stack_list[n-1])
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_roll():
|
|
|
+ #The item n back in the stack is moved to the top.
|
|
|
+
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ n = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(prog_stack.size() < n):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ stack_list = list()
|
|
|
+
|
|
|
+ for i in range(n):
|
|
|
+ stack_list.append(prog_stack.pop())
|
|
|
+
|
|
|
+ for i in range (n-1):
|
|
|
+ prog_stack.push(stack_list[n - 2 - i])
|
|
|
+
|
|
|
+ prog_stack.push(stack_list[n-1])
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_rot():
|
|
|
+ #The top three items on the stack are rotated to the left.
|
|
|
+
|
|
|
+ if(prog_stack.size() < 3):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = prog_stack.pop()
|
|
|
+ x2 = prog_stack.pop()
|
|
|
+ x3 = prog_stack.pop()
|
|
|
+
|
|
|
+ prog_stack.push(x1)
|
|
|
+ prog_stack.push(x3)
|
|
|
+ prog_stack.push(x2)
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_swap():
|
|
|
+ #The top two items on the stack are swapped.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = prog_stack.pop()
|
|
|
+ x2 = prog_stack.pop()
|
|
|
+
|
|
|
+ prog_stack.push(x1)
|
|
|
+ prog_stack.push(x2)
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_tuck():
|
|
|
+ #The item at the top of the stack is copied
|
|
|
+ #and inserted before the second-to-top item.
|
|
|
+ #like cuddling :3 OwO
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = prog_stack.pop()
|
|
|
+ x2 = prog_stack.pop()
|
|
|
+
|
|
|
+ prog_stack.push(x1)
|
|
|
+ prog_stack.push(x2)
|
|
|
+ prog_stack.push(x1)
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_2drop():
|
|
|
+ #Removes the top two stack items.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.pop()
|
|
|
+ prog_stack.pop()
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_2dup():
|
|
|
+ #Duplicates the top two stack items.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = prog_stack.pop()
|
|
|
+ x2 = prog_stack.pop()
|
|
|
+
|
|
|
+ prog_stack.push(x2)
|
|
|
+ prog_stack.push(x1)
|
|
|
+ prog_stack.push(x2)
|
|
|
+ prog_stack.push(x1)
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_3dup():
|
|
|
+ #Duplicates the top three stack items.
|
|
|
+ if(prog_stack.size() < 3):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = prog_stack.pop()
|
|
|
+ x2 = prog_stack.pop()
|
|
|
+ x3 = prog_stack.pop()
|
|
|
+
|
|
|
+ prog_stack.push(x3)
|
|
|
+ prog_stack.push(x2)
|
|
|
+ prog_stack.push(x1)
|
|
|
+ prog_stack.push(x3)
|
|
|
+ prog_stack.push(x2)
|
|
|
+ prog_stack.push(x1)
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_2over():
|
|
|
+ #Copies the pair of items two spaces back in the stack to the front.
|
|
|
+ if(prog_stack.size() < 4):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = prog_stack.pop()
|
|
|
+ x2 = prog_stack.pop()
|
|
|
+ x3 = prog_stack.pop()
|
|
|
+ x4 = prog_stack.pop()
|
|
|
+
|
|
|
+ prog_stack.push(x2)
|
|
|
+ prog_stack.push(x1)
|
|
|
+ prog_stack.push(x4)
|
|
|
+ prog_stack.push(x3)
|
|
|
+ prog_stack.push(x2)
|
|
|
+ prog_stack.push(x1)
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_2rot():
|
|
|
+ #The fifth and sixth items back are moved to the top of the stack.
|
|
|
+ if(prog_stack.size() < 6):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = prog_stack.pop()
|
|
|
+ x2 = prog_stack.pop()
|
|
|
+ x3 = prog_stack.pop()
|
|
|
+ x4 = prog_stack.pop()
|
|
|
+ x5 = prog_stack.pop()
|
|
|
+ x6 = prog_stack.pop()
|
|
|
+
|
|
|
+ prog_stack.push(x2)
|
|
|
+ prog_stack.push(x1)
|
|
|
+ prog_stack.push(x6)
|
|
|
+ prog_stack.push(x5)
|
|
|
+ prog_stack.push(x4)
|
|
|
+ prog_stack.push(x3)
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_2swap():
|
|
|
+ #Swaps the top two pairs of items.
|
|
|
+ if(prog_stack.size() < 4):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = prog_stack.pop()
|
|
|
+ x2 = prog_stack.pop()
|
|
|
+ x3 = prog_stack.pop()
|
|
|
+ x4 = prog_stack.pop()
|
|
|
+
|
|
|
+ prog_stack.push(x2)
|
|
|
+ prog_stack.push(x1)
|
|
|
+ prog_stack.push(x4)
|
|
|
+ prog_stack.push(x3)
|
|
|
+
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_1add():
|
|
|
+ #1 is added to the input.
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = int(prog_stack.pop())
|
|
|
+ x1 += 1
|
|
|
+ prog_stack.push(str(x1))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_1sub():
|
|
|
+ #1 is subtracted from the input.
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = int(prog_stack.pop())
|
|
|
+ x1 -= 1
|
|
|
+ prog_stack.push(str(x1))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_2mul():
|
|
|
+ #The input is multiplied by 2. disabled at BITCOIN.
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = int(prog_stack.pop())
|
|
|
+ x1 *= 2
|
|
|
+ prog_stack.push(str(x1))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_2div():
|
|
|
+ #The input is divided by 2. disabled at BITCOIN.
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = int(prog_stack.pop())
|
|
|
+ x1 /= 2
|
|
|
+ prog_stack.push(str(x1))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_not():
|
|
|
+ #If the input is 0 or 1, it is flipped. Otherwise the output will be 0.
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(x1 == 0):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_0notequal():
|
|
|
+ #Returns 0 if the input is 0. 1 otherwise.
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x1 = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(x1 == 0):
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_add():
|
|
|
+ #a is added to b.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ prog_stack.push(str(a + b))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_sub():
|
|
|
+ #b is subtracted from a.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ prog_stack.push(str(a - b))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_mul():
|
|
|
+ #a is multiplied by b. disabled at BITCOIN.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ prog_stack.push(str(a * b))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_div():
|
|
|
+ #a is divided by b. disabled at BITCOIN.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ prog_stack.push(str(a / b))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_mod():
|
|
|
+ #Returns the remainder after dividing a by b. disabled at BITCOIN.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ prog_stack.push(str(a % b))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_booland():
|
|
|
+ #If both a and b are not "" (null string), the output is 1. Otherwise 0.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = prog_stack.pop()
|
|
|
+ b = prog_stack.pop()
|
|
|
+
|
|
|
+ if(a != "" and b != ""):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_boolor():
|
|
|
+ #If a or b is not "" (null string), the output is 1. Otherwise 0.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = prog_stack.pop()
|
|
|
+ b = prog_stack.pop()
|
|
|
+
|
|
|
+ if(a != "" or b != ""):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ op_false()
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_numequal():
|
|
|
+ #Returns 1 if the numbers are equal, 0 otherwise.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(a == b):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_numequalverify():
|
|
|
+ #Same as OP_NUMEQUAL, but runs OP_VERIFY afterward.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(a == b):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(0))
|
|
|
+
|
|
|
+ #verify
|
|
|
+ if(prog_stack.is_empty()):
|
|
|
+ logging.warning("Stack is empty")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ if(not(int(prog_stack.pop()))):
|
|
|
+ logging.warning('Transaction not valid!')
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_numnotequal():
|
|
|
+ #Returns 1 if the numbers are not equal, 0 otherwise.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(a != b):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_lessthan():
|
|
|
+ #Returns 1 if a is less than b, 0 otherwise.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(a < b):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_greaterthan():
|
|
|
+ #Returns 1 if a is greater than b, 0 otherwise.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(a > b):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_lessthanorequal():
|
|
|
+ #Returns 1 if a is less than or equal to b, 0 otherwise.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(a <= b):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_greaterthanorequal():
|
|
|
+ #Returns 1 if a is greater than or equal to b, 0 otherwise.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(a >= b):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_min():
|
|
|
+ #Returns the smaller of a and b.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(a < b):
|
|
|
+ prog_stack.push(str(a))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(b))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_max():
|
|
|
+ #Returns the larger of a and b.
|
|
|
+ if(prog_stack.size() < 2):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ a = int(prog_stack.pop())
|
|
|
+ b = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(a > b):
|
|
|
+ prog_stack.push(str(a))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(b))
|
|
|
+ return
|
|
|
+
|
|
|
+ def op_within():
|
|
|
+ #Returns 1 if x is within the specified range (left-inclusive), 0 otherwise.
|
|
|
+ if(prog_stack.size() < 3):
|
|
|
+ logging.warning("Not enough arguments")
|
|
|
+ invalidate()
|
|
|
+ return
|
|
|
+
|
|
|
+ x = int(prog_stack.pop())
|
|
|
+ mini = int(prog_stack.pop())
|
|
|
+ maxi = int(prog_stack.pop())
|
|
|
+
|
|
|
+ if(mini <= x and x < maxi):
|
|
|
+ prog_stack.push(str(1))
|
|
|
+ return
|
|
|
+
|
|
|
+ prog_stack.push(str(0))
|
|
|
+ return
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ """
|
|
|
+ OPLIST:
|
|
|
+ This is the dictionary used by the fetch-and-execute loop to parse
|
|
|
+ opcode strings into proper operations. Proper operations will then be
|
|
|
+ executed. Any command string that matches one of the opcodes will be
|
|
|
+ treated like an operation.
|
|
|
+ """
|
|
|
+ global operations
|
|
|
+ operations = {
|
|
|
+ # opcode # proper operation
|
|
|
+ 'OP_RIPEMD160': op_ripemd160,
|
|
|
+ 'OP_SHA1': op_sha1,
|
|
|
+ 'OP_SHA256': op_sha256,
|
|
|
+ 'OP_HASH256': op_hash256,
|
|
|
+ 'OP_HASH160': op_hash160,
|
|
|
+ 'OP_VERIFYSIG': op_checksig,
|
|
|
+ 'OP_CHECKSIG': op_checksig,
|
|
|
+ 'OP_CHECKSIGVERIFY': op_checksigverify,
|
|
|
+
|
|
|
+ 'OP_NOP': op_nop,
|
|
|
+ 'OP_EQUAL': op_equal,
|
|
|
+ 'OP_VERIFY': op_verify,
|
|
|
+ 'OP_IF': op_if,
|
|
|
+ 'OP_NOTIF': op_notif,
|
|
|
+ 'OP_ELSE': op_else,
|
|
|
+ 'OP_ENDIF': op_endif,
|
|
|
+ 'OP_RETURN': op_return,
|
|
|
+
|
|
|
+ 'OP_DUP': op_dup,
|
|
|
+ 'OP_DROP': op_drop,
|
|
|
+
|
|
|
+ 'OP_CHECKLOCKTIME': op_checklocktime,
|
|
|
+ 'OP_CHECKLOCKTIMEVERIFY': op_checklocktime,
|
|
|
+ 'OP_NOP2': op_checklocktime,
|
|
|
+
|
|
|
+ 'OP_EQUALVERIFY': op_equalverify,
|
|
|
+ 'OP_INVERT': op_invert,
|
|
|
+ 'OP_AND': op_and,
|
|
|
+ 'OP_OR': op_or,
|
|
|
+ 'OP_XOR': op_xor,
|
|
|
+ 'OP_TRUE': op_true,
|
|
|
+ 'OP_1': op_true,
|
|
|
+ 'OP_FALSE': op_false,
|
|
|
+ 'OP_0': op_false,
|
|
|
+ 'OP_1NEGATE': op_1negate,
|
|
|
+
|
|
|
+ 'OP_TOALTSTACK': op_toaltstack,
|
|
|
+ 'OP_FROMALTSTACK': op_fromaltstack,
|
|
|
+ 'OP_IFDUP': op_ifdup,
|
|
|
+ 'OP_NIP': op_nip,
|
|
|
+ 'OP_OVER': op_over,
|
|
|
+ 'OP_PICK': op_pick,
|
|
|
+ 'OP_ROLL': op_roll,
|
|
|
+ 'OP_ROT': op_rot,
|
|
|
+ 'OP_SWAP': op_swap,
|
|
|
+ 'OP_TUCK': op_tuck,
|
|
|
+ 'OP_2DROP': op_2drop,
|
|
|
+ 'OP_2DUP': op_2dup,
|
|
|
+ 'OP_3DUP': op_3dup,
|
|
|
+ 'OP_2OVER': op_2over,
|
|
|
+ 'OP_2ROT': op_2rot,
|
|
|
+ 'OP_2SWAP': op_2swap,
|
|
|
+
|
|
|
+ 'OP_1ADD': op_1add,
|
|
|
+ 'OP_1SUB': op_1sub,
|
|
|
+ 'OP_2MUL': op_2mul,
|
|
|
+ 'OP_2DIV': op_2div,
|
|
|
+ 'OP_NOT': op_not,
|
|
|
+ 'OP_0NOTEQUAL': op_0notequal,
|
|
|
+ 'OP_ADD': op_add,
|
|
|
+ 'OP_SUB': op_sub,
|
|
|
+ 'OP_MUL': op_mul,
|
|
|
+ 'OP_DIV': op_div,
|
|
|
+ 'OP_MOD': op_mod,
|
|
|
+ 'OP_BOOLAND': op_booland,
|
|
|
+ 'OP_BOOLOR': op_boolor,
|
|
|
+ 'OP_NUMEQUAL': op_numequal,
|
|
|
+ 'OP_NUMEQUALVERIFY': op_numequalverify,
|
|
|
+ 'OP_NUMNOTEQUAL': op_numnotequal,
|
|
|
+ 'OP_LESSTHAN': op_lessthan,
|
|
|
+ 'OP_GREATERTHAN': op_greaterthan,
|
|
|
+ 'OP_LESSTHANOREQUAL': op_lessthanorequal,
|
|
|
+ 'OP_GREATERTHANOREQUAL': op_greaterthanorequal,
|
|
|
+ 'OP_MIN': op_min,
|
|
|
+ 'OP_MAX': op_max,
|
|
|
+ 'OP_WITHIN': op_within,
|
|
|
+
|
|
|
+ 'OP_NOP1': op_nop,
|
|
|
+ 'OP_NOP4': op_nop,
|
|
|
+ 'OP_NOP5': op_nop,
|
|
|
+ 'OP_NOP6': op_nop,
|
|
|
+ 'OP_NOP7': op_nop,
|
|
|
+ 'OP_NOP8': op_nop,
|
|
|
+ 'OP_NOP9': op_nop,
|
|
|
+ 'OP_NOP10': op_nop,
|
|
|
+ }
|
|
|
+
|
|
|
+ def if_endif_syntax_check(self):
|
|
|
+ """
|
|
|
+ Checks whether the program contains proper if/endif syntax as
|
|
|
+ specified in the documentation.
|
|
|
+ """
|
|
|
+ global operations
|
|
|
+
|
|
|
+ counter = 0
|
|
|
+
|
|
|
+ pq_copy = self.prog_queue.copy()
|
|
|
+ # go through the program queue and convert all opcodes into
|
|
|
+ # proper operations
|
|
|
+
|
|
|
+ ifs = {'OP_IF', 'OP_NOTIF'}
|
|
|
+ elses = {'OP_ELSE'}
|
|
|
+ endifs = {'OP_ENDIF'}
|
|
|
+
|
|
|
+ # Ifs increase the counter, endifs decrease it. Program is only valid
|
|
|
+ # when the counter is 0 at the end and all existing elses occured
|
|
|
+ # when the counter was greater than zero.
|
|
|
+ for command in pq_copy:
|
|
|
+ if command in ifs:
|
|
|
+ counter += 1
|
|
|
+ if command in endifs:
|
|
|
+ counter -= 1
|
|
|
+ if command in elses and counter == 0:
|
|
|
+ invalidate()
|
|
|
+
|
|
|
+ if counter != 0:
|
|
|
+ invalidate()
|
|
|
+
|
|
|
+
|
|
|
+ def execute_script(self, prog_stacky: LabStack, printstack = 0):
|
|
|
+ """
|
|
|
+ Calling this instance method upon a LabScript instance executes the
|
|
|
+ script.
|
|
|
+ """
|
|
|
+ global prog_stack
|
|
|
+ prog_stack = prog_stacky
|
|
|
+ global validity
|
|
|
+ validity = True
|
|
|
+
|
|
|
+ global alt_stack
|
|
|
+ alt_stack = LabStack() # program memory
|
|
|
+
|
|
|
+ global control_flow_stack
|
|
|
+ control_flow_stack = LabStack()
|
|
|
+
|
|
|
+ """
|
|
|
+ stack used for control flow. The topmost entry informs the program
|
|
|
+ whether the oncoming flow of operations can be executed, as allowed
|
|
|
+ or disallowed by any recent conditional operations. this acts like
|
|
|
+ a state machine. We need these flags to prevent execution of else
|
|
|
+ statements across layered if-blocks.
|
|
|
+ """
|
|
|
+
|
|
|
+ ALLOW_ALL=0 # Any operation can be executed.
|
|
|
+ ALLOW_IF_ELSE=1 # Nothing but ifs, elses and endifs can be executed.
|
|
|
+ ALLOW_IF=2 # Nothing but ifs and endifs can be executed.
|
|
|
+ # To understand what all of this crap means, execute this code on
|
|
|
+ # paper and write down what flags are pushed when:
|
|
|
+ """
|
|
|
+ OP_IF (true)
|
|
|
+ OP_NOP
|
|
|
+ OP_ELSE
|
|
|
+ OP_IF
|
|
|
+ OP_NOP
|
|
|
+ OP_ELSE
|
|
|
+ OP_NOP
|
|
|
+ OP_ENDIF
|
|
|
+ OP_ELSE
|
|
|
+ OP_NOP
|
|
|
+ OP_ENDIF
|
|
|
+ """
|
|
|
+
|
|
|
+ control_flow_stack.push(ALLOW_ALL)
|
|
|
+
|
|
|
+
|
|
|
+ #This loop keeps fetching commands from the prog_queue and tries to
|
|
|
+ #interpret and execute them, until end of program is reached.
|
|
|
+ while(self.pc <= self.pend):
|
|
|
+
|
|
|
+ #global validity
|
|
|
+ self.valid = validity
|
|
|
+ # Check for validity. If invalid, stop executing.
|
|
|
+ if not self.valid:
|
|
|
+ logging.warning("[!] Error: Invalid.")
|
|
|
+ return 0
|
|
|
+
|
|
|
+ next_item = self.prog_queue[self.pc-1] # Fetch next item
|
|
|
+ operationstring = next_item
|
|
|
+
|
|
|
+ pushed = 0
|
|
|
+
|
|
|
+ # Check if item is data or opcode. If data, push onto stack.
|
|
|
+ if (next_item not in operations):
|
|
|
+ if control_flow_stack.peek() == ALLOW_ALL:
|
|
|
+ prog_stack.push(next_item)
|
|
|
+ pushed = 1
|
|
|
+
|
|
|
+ #print
|
|
|
+ if(printstack):
|
|
|
+ print("--------------------------")
|
|
|
+ print("SCRIPT: \"" + self.to_string() + "\"")
|
|
|
+ print("--------------------------")
|
|
|
+ if(pushed):
|
|
|
+ print("PUSHED:")
|
|
|
+ else:
|
|
|
+ print("DIDN'T PUSH:")
|
|
|
+ print(str(operationstring) + "\n")
|
|
|
+ print("COUNTER:")
|
|
|
+ print("programm_end: " + str(self.pend))
|
|
|
+ print("programm_step: " + str(self.pc))
|
|
|
+ print("\n")
|
|
|
+ print("STACK:")
|
|
|
+ prog_stack.print_stack()
|
|
|
+ print("\nCONTROL_FLOW_STACK:")
|
|
|
+ control_flow_stack.print_stack()
|
|
|
+ print("--------------------------")
|
|
|
+ #input()
|
|
|
+
|
|
|
+ self.pc = self.pc + 1
|
|
|
+
|
|
|
+ continue
|
|
|
+
|
|
|
+ op = operations[next_item] # Proper operation to be executed
|
|
|
+
|
|
|
+ # Check if op_code is op_if or op_endif. Always execute
|
|
|
+ # these operations.
|
|
|
+ if operationstring in {'OP_IF', 'OP_ENDIF'}:
|
|
|
+ op() # EXECUTION
|
|
|
+
|
|
|
+ # Check if op_code is op_else. Only allow this operation if
|
|
|
+ # the control flow flag is ALLOW_ELSE.
|
|
|
+ elif control_flow_stack.peek() == ALLOW_IF_ELSE:
|
|
|
+ if operationstring == 'OP_ELSE':
|
|
|
+ op() # EXECUTION
|
|
|
+
|
|
|
+ # If op_code is any other proper operation, execute it only
|
|
|
+ # if the current control flow allows it. It won't get performed
|
|
|
+ # if we are in an if/else-block that was evaluated to false.
|
|
|
+ elif control_flow_stack.peek() == ALLOW_ALL:
|
|
|
+ op() # EXECUTION
|
|
|
+
|
|
|
+ #if the printstack argument is given, the stack is printed after each step.
|
|
|
+
|
|
|
+ if(printstack):
|
|
|
+ print("--------------------------")
|
|
|
+ print("SCRIPT: \"" + self.to_string() + "\"")
|
|
|
+ print("--------------------------")
|
|
|
+ print("OPERATION:")
|
|
|
+ print(str(operationstring) + "\n")
|
|
|
+ print("COUNTER:")
|
|
|
+ print("programm_end: " + str(self.pend))
|
|
|
+ print("programm_step: " + str(self.pc) + "\n")
|
|
|
+ print("STACK:")
|
|
|
+ prog_stack.print_stack()
|
|
|
+ print("\nCONTROL_FLOW_STACK:")
|
|
|
+ control_flow_stack.print_stack()
|
|
|
+ print("--------------------------")
|
|
|
+ #input()
|
|
|
+
|
|
|
+ self.pc= self.pc + 1
|
|
|
+
|
|
|
+ # If Progstack is empty or TRUE is on top return true otherwise
|
|
|
+ # invalidate and return FALSE
|
|
|
+ if (prog_stack.is_empty() or (prog_stack.size() == 1 and prog_stack.peek() == '1')):
|
|
|
+ return 1
|
|
|
+ else:
|
|
|
+ invalidate()
|
|
|
+ logging.warning("[!] Error: Invalid Tx.")
|
|
|
+ return 0
|
|
|
+
|
|
|
+
|