import struct
from sys import stdout
from binascii import hexlify
from .bignum import bn2vch
long = int
def bchr(x):
return bytes([x])
def bord(x):
return x
MAX_SCRIPT_SIZE = 10000
MAX_SCRIPT_ELEMENT_SIZE = 520
MAX_SCRIPT_OPCODES = 201
OPCODE_NAMES = {}
_opcode_instances = []
class CScriptOp(int):
__slots__ = []
@staticmethod
def encode_op_pushdata(d):
if len(d) < 0x4C:
return b"" + bchr(len(d)) + d if len(d) <= 0xFF:
return b"\x4c" + bchr(len(d)) + d if len(d) <= 0xFFFF:
return b"\x4d" + struct.pack(b"<H", len(d)) + d if len(d) <= 0xFFFFFFFF:
return b"\x4e" + struct.pack(b"<I", len(d)) + d
raise ValueError("Data too long to encode in a PUSHDATA op")
@staticmethod
def encode_op_n(n):
if not 0 <= n <= 16:
raise ValueError(f"Integer must be in range 0 <= n <= 16, got {n}")
if n == 0:
return OP_0
return CScriptOp(OP_1 + n - 1)
def decode_op_n(self):
if self == OP_0:
return 0
if not (self == OP_0 or OP_1 <= self <= OP_16):
raise ValueError(f"op {self!r} is not an OP_N")
return int(self - OP_1 + 1)
def is_small_int(self):
return 0x51 <= self <= 0x60 or self == 0
def to_hex(self):
return hexlify(self.to_bytes(1, "little")).decode()
def to_bin(self):
return self.to_bytes(1, "little")
def __str__(self):
return repr(self)
def __repr__(self):
if self in OPCODE_NAMES:
return OPCODE_NAMES[self]
return f"CScriptOp(0x{self:x})"
def __new__(cls, n):
try:
return _opcode_instances[n]
except IndexError:
assert len(_opcode_instances) == n
_opcode_instances.append(super(CScriptOp, cls).__new__(cls, n))
return _opcode_instances[n]
for op_number in range(0xFF + 1):
CScriptOp(op_number)
OP_0 = CScriptOp(0x00)
OP_FALSE = OP_0
OP_PUSHDATA1 = CScriptOp(0x4C)
OP_PUSHDATA2 = CScriptOp(0x4D)
OP_PUSHDATA4 = CScriptOp(0x4E)
OP_1_NEGATE = CScriptOp(0x4F)
OP_RESERVED = CScriptOp(0x50)
OP_1 = CScriptOp(0x51)
OP_TRUE = OP_1
OP_2 = CScriptOp(0x52)
OP_3 = CScriptOp(0x53)
OP_4 = CScriptOp(0x54)
OP_5 = CScriptOp(0x55)
OP_6 = CScriptOp(0x56)
OP_7 = CScriptOp(0x57)
OP_8 = CScriptOp(0x58)
OP_9 = CScriptOp(0x59)
OP_10 = CScriptOp(0x5A)
OP_11 = CScriptOp(0x5B)
OP_12 = CScriptOp(0x5C)
OP_13 = CScriptOp(0x5D)
OP_14 = CScriptOp(0x5E)
OP_15 = CScriptOp(0x5F)
OP_16 = CScriptOp(0x60)
OP_NOP = CScriptOp(0x61)
OP_VER = CScriptOp(0x62)
OP_IF = CScriptOp(0x63)
OP_NOTIF = CScriptOp(0x64)
OP_VERIF = CScriptOp(0x65)
OP_VERNOTIF = CScriptOp(0x66)
OP_ELSE = CScriptOp(0x67)
OP_ENDIF = CScriptOp(0x68)
OP_VERIFY = CScriptOp(0x69)
OP_RETURN = CScriptOp(0x6A)
OP_TOALTSTACK = CScriptOp(0x6B)
OP_FROMALTSTACK = CScriptOp(0x6C)
OP_2_DROP = CScriptOp(0x6D)
OP_2_DUP = CScriptOp(0x6E)
OP_3_DUP = CScriptOp(0x6F)
OP_2_OVER = CScriptOp(0x70)
OP_2_ROT = CScriptOp(0x71)
OP_2_SWAP = CScriptOp(0x72)
OP_IFDUP = CScriptOp(0x73)
OP_DEPTH = CScriptOp(0x74)
OP_DROP = CScriptOp(0x75)
OP_DUP = CScriptOp(0x76)
OP_NIP = CScriptOp(0x77)
OP_OVER = CScriptOp(0x78)
OP_PICK = CScriptOp(0x79)
OP_ROLL = CScriptOp(0x7A)
OP_ROT = CScriptOp(0x7B)
OP_SWAP = CScriptOp(0x7C)
OP_TUCK = CScriptOp(0x7D)
OP_CAT = CScriptOp(0x7E)
OP_SPLIT = CScriptOp(0x7F)
OP_NUM2_BIN = CScriptOp(0x80)
OP_BIN2_NUM = CScriptOp(0x81)
OP_SIZE = CScriptOp(0x82)
OP_INVERT = CScriptOp(0x83)
OP_AND = CScriptOp(0x84)
OP_OR = CScriptOp(0x85)
OP_XOR = CScriptOp(0x86)
OP_EQUAL = CScriptOp(0x87)
OP_EQUALVERIFY = CScriptOp(0x88)
OP_RESERVED1 = CScriptOp(0x89)
OP_RESERVED2 = CScriptOp(0x8A)
OP_1_ADD = CScriptOp(0x8B)
OP_1_SUB = CScriptOp(0x8C)
OP_2_MUL = CScriptOp(0x8D)
OP_2_DIV = CScriptOp(0x8E)
OP_NEGATE = CScriptOp(0x8F)
OP_ABS = CScriptOp(0x90)
OP_NOT = CScriptOp(0x91)
OP_0_NOTEQUAL = CScriptOp(0x92)
OP_ADD = CScriptOp(0x93)
OP_SUB = CScriptOp(0x94)
OP_MUL = CScriptOp(0x95)
OP_DIV = CScriptOp(0x96)
OP_MOD = CScriptOp(0x97)
OP_LSHIFT = CScriptOp(0x98)
OP_RSHIFT = CScriptOp(0x99)
OP_BOOLAND = CScriptOp(0x9A)
OP_BOOLOR = CScriptOp(0x9B)
OP_NUMEQUAL = CScriptOp(0x9C)
OP_NUMEQUALVERIFY = CScriptOp(0x9D)
OP_NUMNOTEQUAL = CScriptOp(0x9E)
OP_LESSTHAN = CScriptOp(0x9F)
OP_GREATERTHAN = CScriptOp(0xA0)
OP_LESSTHANOREQUAL = CScriptOp(0xA1)
OP_GREATERTHANOREQUAL = CScriptOp(0xA2)
OP_MIN = CScriptOp(0xA3)
OP_MAX = CScriptOp(0xA4)
OP_WITHIN = CScriptOp(0xA5)
OP_RIPEMD160 = CScriptOp(0xA6)
OP_SHA1 = CScriptOp(0xA7)
OP_SHA256 = CScriptOp(0xA8)
OP_HASH160 = CScriptOp(0xA9)
OP_HASH256 = CScriptOp(0xAA)
OP_CODESEPARATOR = CScriptOp(0xAB)
OP_CHECKSIG = CScriptOp(0xAC)
OP_CHECKSIGVERIFY = CScriptOp(0xAD)
OP_CHECKMULTISIG = CScriptOp(0xAE)
OP_CHECKMULTISIGVERIFY = CScriptOp(0xAF)
OP_CHECKDATASIG = CScriptOp(0xBA)
OP_CHECKDATASIGVERIFY = CScriptOp(0xBB)
OP_REVERSEBYTES = CScriptOp(0xBC)
OP_NOP1 = CScriptOp(0xB0)
OP_CHECKLOCKTIMEVERIFY = CScriptOp(0xB1)
OP_CHECKSEQUENCEVERIFY = CScriptOp(0xB2)
OP_NOP4 = CScriptOp(0xB3)
OP_NOP5 = CScriptOp(0xB4)
OP_NOP6 = CScriptOp(0xB5)
OP_NOP7 = CScriptOp(0xB6)
OP_NOP8 = CScriptOp(0xB7)
OP_NOP9 = CScriptOp(0xB8)
OP_NOP10 = CScriptOp(0xB9)
OP_INPUTINDEX = CScriptOp(0xC0)
OP_ACTIVEBYTECODE = CScriptOp(0xC1)
OP_TXVERSION = CScriptOp(0xC2)
OP_TXINPUTCOUNT = CScriptOp(0xC3)
OP_TXOUTPUTCOUNT = CScriptOp(0xC4)
OP_TXLOCKTIME = CScriptOp(0xC5)
OP_UTXOVALUE = CScriptOp(0xC6)
OP_UTXOBYTECODE = CScriptOp(0xC7)
OP_OUTPOINTTXHASH = CScriptOp(0xC8)
OP_OUTPOINTINDEX = CScriptOp(0xC9)
OP_INPUTBYTECODE = CScriptOp(0xCA)
OP_INPUTSEQUENCENUMBER = CScriptOp(0xCB)
OP_OUTPUTVALUE = CScriptOp(0xCC)
OP_OUTPUTBYTECODE = CScriptOp(0xCD)
OP_NATIVE_INTROSPECTION_RESERVED1 = CScriptOp(0xCE)
OP_NATIVE_INTROSPECTION_RESERVED2 = CScriptOp(0xCF)
OP_PLACE = CScriptOp(0xE9)
OP_PUSH_TX_STATE = CScriptOp(0xEA)
OP_SETBMD = CScriptOp(0xEB)
OP_BIN2_BIGNUM = CScriptOp(0xEC)
OP_EXEC = CScriptOp(0xED)
OP_GROUP = CScriptOp(0xEE)
OP_TEMPLATE = CScriptOp(0xEF)
OP_SMALLINTEGER = CScriptOp(0xFA)
OP_PUBKEYS = CScriptOp(0xFB)
OP_PUBKEYHASH = CScriptOp(0xFD)
OP_PUBKEY = CScriptOp(0xFE)
OP_INVALIDOPCODE = CScriptOp(0xFF)
VALID_OPCODES = {
OP_1_NEGATE,
OP_RESERVED,
OP_1,
OP_2,
OP_3,
OP_4,
OP_5,
OP_6,
OP_7,
OP_8,
OP_9,
OP_10,
OP_11,
OP_12,
OP_13,
OP_14,
OP_15,
OP_16,
OP_NOP,
OP_VER,
OP_IF,
OP_NOTIF,
OP_VERIF,
OP_VERNOTIF,
OP_ELSE,
OP_ENDIF,
OP_VERIFY,
OP_RETURN,
OP_TOALTSTACK,
OP_FROMALTSTACK,
OP_2_DROP,
OP_2_DUP,
OP_3_DUP,
OP_2_OVER,
OP_2_ROT,
OP_2_SWAP,
OP_IFDUP,
OP_DEPTH,
OP_DROP,
OP_DUP,
OP_NIP,
OP_OVER,
OP_PICK,
OP_ROLL,
OP_ROT,
OP_SWAP,
OP_TUCK,
OP_CAT,
OP_SPLIT,
OP_NUM2_BIN,
OP_BIN2_NUM,
OP_SIZE,
OP_INVERT,
OP_AND,
OP_OR,
OP_XOR,
OP_EQUAL,
OP_EQUALVERIFY,
OP_RESERVED1,
OP_RESERVED2,
OP_1_ADD,
OP_1_SUB,
OP_2_MUL,
OP_2_DIV,
OP_NEGATE,
OP_ABS,
OP_NOT,
OP_0_NOTEQUAL,
OP_ADD,
OP_SUB,
OP_MUL,
OP_DIV,
OP_MOD,
OP_LSHIFT,
OP_RSHIFT,
OP_BOOLAND,
OP_BOOLOR,
OP_NUMEQUAL,
OP_NUMEQUALVERIFY,
OP_NUMNOTEQUAL,
OP_LESSTHAN,
OP_GREATERTHAN,
OP_LESSTHANOREQUAL,
OP_GREATERTHANOREQUAL,
OP_MIN,
OP_MAX,
OP_WITHIN,
OP_RIPEMD160,
OP_SHA1,
OP_SHA256,
OP_HASH160,
OP_HASH256,
OP_CODESEPARATOR,
OP_CHECKSIG,
OP_CHECKSIGVERIFY,
OP_CHECKMULTISIG,
OP_CHECKMULTISIGVERIFY,
OP_CHECKDATASIG,
OP_CHECKDATASIGVERIFY,
OP_NOP1,
OP_CHECKLOCKTIMEVERIFY,
OP_CHECKSEQUENCEVERIFY,
OP_NOP4,
OP_NOP5,
OP_NOP6,
OP_NOP7,
OP_NOP8,
OP_NOP9,
OP_NOP10,
OP_INPUTINDEX,
OP_ACTIVEBYTECODE,
OP_TXVERSION,
OP_TXINPUTCOUNT,
OP_TXOUTPUTCOUNT,
OP_TXLOCKTIME,
OP_UTXOVALUE,
OP_UTXOBYTECODE,
OP_OUTPOINTTXHASH,
OP_OUTPOINTINDEX,
OP_INPUTBYTECODE,
OP_INPUTSEQUENCENUMBER,
OP_OUTPUTVALUE,
OP_OUTPUTBYTECODE,
OP_NATIVE_INTROSPECTION_RESERVED1,
OP_NATIVE_INTROSPECTION_RESERVED2,
OP_PLACE,
OP_PUSH_TX_STATE,
OP_SETBMD,
OP_BIN2_BIGNUM,
OP_EXEC,
OP_GROUP,
OP_TEMPLATE,
OP_SMALLINTEGER,
OP_PUBKEYS,
OP_PUBKEYHASH,
OP_PUBKEY,
}
OPCODE_NAMES.update(
{
OP_0: "OP_0",
OP_PUSHDATA1: "OP_PUSHDATA1",
OP_PUSHDATA2: "OP_PUSHDATA2",
OP_PUSHDATA4: "OP_PUSHDATA4",
OP_1_NEGATE: "OP_1_NEGATE",
OP_RESERVED: "OP_RESERVED",
OP_1: "OP_1",
OP_2: "OP_2",
OP_3: "OP_3",
OP_4: "OP_4",
OP_5: "OP_5",
OP_6: "OP_6",
OP_7: "OP_7",
OP_8: "OP_8",
OP_9: "OP_9",
OP_10: "OP_10",
OP_11: "OP_11",
OP_12: "OP_12",
OP_13: "OP_13",
OP_14: "OP_14",
OP_15: "OP_15",
OP_16: "OP_16",
OP_NOP: "OP_NOP",
OP_VER: "OP_VER",
OP_IF: "OP_IF",
OP_NOTIF: "OP_NOTIF",
OP_VERIF: "OP_VERIF",
OP_VERNOTIF: "OP_VERNOTIF",
OP_ELSE: "OP_ELSE",
OP_ENDIF: "OP_ENDIF",
OP_VERIFY: "OP_VERIFY",
OP_RETURN: "OP_RETURN",
OP_TOALTSTACK: "OP_TOALTSTACK",
OP_FROMALTSTACK: "OP_FROMALTSTACK",
OP_2_DROP: "OP_2_DROP",
OP_2_DUP: "OP_2_DUP",
OP_3_DUP: "OP_3_DUP",
OP_2_OVER: "OP_2_OVER",
OP_2_ROT: "OP_2_ROT",
OP_2_SWAP: "OP_2_SWAP",
OP_IFDUP: "OP_IFDUP",
OP_DEPTH: "OP_DEPTH",
OP_DROP: "OP_DROP",
OP_DUP: "OP_DUP",
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_CAT: "OP_CAT",
OP_SPLIT: "OP_SPLIT",
OP_NUM2_BIN: "OP_NUM2_BIN",
OP_BIN2_NUM: "OP_BIN2_NUM",
OP_SIZE: "OP_SIZE",
OP_INVERT: "OP_INVERT",
OP_AND: "OP_AND",
OP_OR: "OP_OR",
OP_XOR: "OP_XOR",
OP_EQUAL: "OP_EQUAL",
OP_EQUALVERIFY: "OP_EQUALVERIFY",
OP_RESERVED1: "OP_RESERVED1",
OP_RESERVED2: "OP_RESERVED2",
OP_1_ADD: "OP_1_ADD",
OP_1_SUB: "OP_1_SUB",
OP_2_MUL: "OP_2_MUL",
OP_2_DIV: "OP_2_DIV",
OP_NEGATE: "OP_NEGATE",
OP_ABS: "OP_ABS",
OP_NOT: "OP_NOT",
OP_0_NOTEQUAL: "OP_0_NOTEQUAL",
OP_ADD: "OP_ADD",
OP_SUB: "OP_SUB",
OP_MUL: "OP_MUL",
OP_DIV: "OP_DIV",
OP_MOD: "OP_MOD",
OP_LSHIFT: "OP_LSHIFT",
OP_RSHIFT: "OP_RSHIFT",
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_RIPEMD160: "OP_RIPEMD160",
OP_SHA1: "OP_SHA1",
OP_SHA256: "OP_SHA256",
OP_HASH160: "OP_HASH160",
OP_HASH256: "OP_HASH256",
OP_CODESEPARATOR: "OP_CODESEPARATOR",
OP_CHECKSIG: "OP_CHECKSIG",
OP_CHECKSIGVERIFY: "OP_CHECKSIGVERIFY",
OP_CHECKMULTISIG: "OP_CHECKMULTISIG",
OP_CHECKMULTISIGVERIFY: "OP_CHECKMULTISIGVERIFY",
OP_CHECKDATASIG: "OP_CHECKDATASIG",
OP_CHECKDATASIGVERIFY: "OP_CHECKDATASIGVERIFY",
OP_NOP1: "OP_NOP1",
OP_CHECKLOCKTIMEVERIFY: "OP_CHECKLOCKTIMEVERIFY",
OP_CHECKSEQUENCEVERIFY: "OP_CHECKSEQUENCEVERIFY",
OP_NOP4: "OP_NOP4",
OP_NOP5: "OP_NOP5",
OP_NOP6: "OP_NOP6",
OP_NOP7: "OP_NOP7",
OP_NOP8: "OP_NOP8",
OP_NOP9: "OP_NOP9",
OP_NOP10: "OP_NOP10",
OP_PLACE: "OP_PLACE",
OP_PUSH_TX_STATE: "OP_PUSH_TX_STATE",
OP_SETBMD: "OP_SETBMD",
OP_BIN2_BIGNUM: "OP_BIN2_BIGNUM",
OP_EXEC: "OP_EXEC",
OP_GROUP: "OP_GROUP",
OP_TEMPLATE: "OP_TEMPLATE",
OP_SMALLINTEGER: "OP_SMALLINTEGER",
OP_PUBKEYS: "OP_PUBKEYS",
OP_PUBKEYHASH: "OP_PUBKEYHASH",
OP_PUBKEY: "OP_PUBKEY",
OP_INVALIDOPCODE: "OP_INVALIDOPCODE",
}
)
OPCODES_BY_NAME = {
"OP_0": OP_0,
"OP_PUSHDATA1": OP_PUSHDATA1,
"OP_PUSHDATA2": OP_PUSHDATA2,
"OP_PUSHDATA4": OP_PUSHDATA4,
"OP_1_NEGATE": OP_1_NEGATE,
"OP_RESERVED": OP_RESERVED,
"OP_1": OP_1,
"OP_2": OP_2,
"OP_3": OP_3,
"OP_4": OP_4,
"OP_5": OP_5,
"OP_6": OP_6,
"OP_7": OP_7,
"OP_8": OP_8,
"OP_9": OP_9,
"OP_10": OP_10,
"OP_11": OP_11,
"OP_12": OP_12,
"OP_13": OP_13,
"OP_14": OP_14,
"OP_15": OP_15,
"OP_16": OP_16,
"OP_NOP": OP_NOP,
"OP_VER": OP_VER,
"OP_IF": OP_IF,
"OP_NOTIF": OP_NOTIF,
"OP_VERIF": OP_VERIF,
"OP_VERNOTIF": OP_VERNOTIF,
"OP_ELSE": OP_ELSE,
"OP_ENDIF": OP_ENDIF,
"OP_VERIFY": OP_VERIFY,
"OP_RETURN": OP_RETURN,
"OP_TOALTSTACK": OP_TOALTSTACK,
"OP_FROMALTSTACK": OP_FROMALTSTACK,
"OP_2_DROP": OP_2_DROP,
"OP_2_DUP": OP_2_DUP,
"OP_3_DUP": OP_3_DUP,
"OP_2_OVER": OP_2_OVER,
"OP_2_ROT": OP_2_ROT,
"OP_2_SWAP": OP_2_SWAP,
"OP_IFDUP": OP_IFDUP,
"OP_DEPTH": OP_DEPTH,
"OP_DROP": OP_DROP,
"OP_DUP": OP_DUP,
"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_CAT": OP_CAT,
"OP_SPLIT": OP_SPLIT,
"OP_NUM2_BIN": OP_NUM2_BIN,
"OP_BIN2_NUM": OP_BIN2_NUM,
"OP_SIZE": OP_SIZE,
"OP_INVERT": OP_INVERT,
"OP_AND": OP_AND,
"OP_OR": OP_OR,
"OP_XOR": OP_XOR,
"OP_EQUAL": OP_EQUAL,
"OP_EQUALVERIFY": OP_EQUALVERIFY,
"OP_RESERVED1": OP_RESERVED1,
"OP_RESERVED2": OP_RESERVED2,
"OP_1_ADD": OP_1_ADD,
"OP_1_SUB": OP_1_SUB,
"OP_2_MUL": OP_2_MUL,
"OP_2_DIV": OP_2_DIV,
"OP_NEGATE": OP_NEGATE,
"OP_ABS": OP_ABS,
"OP_NOT": OP_NOT,
"OP_0_NOTEQUAL": OP_0_NOTEQUAL,
"OP_ADD": OP_ADD,
"OP_SUB": OP_SUB,
"OP_MUL": OP_MUL,
"OP_DIV": OP_DIV,
"OP_MOD": OP_MOD,
"OP_LSHIFT": OP_LSHIFT,
"OP_RSHIFT": OP_RSHIFT,
"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_RIPEMD160": OP_RIPEMD160,
"OP_SHA1": OP_SHA1,
"OP_SHA256": OP_SHA256,
"OP_HASH160": OP_HASH160,
"OP_HASH256": OP_HASH256,
"OP_CODESEPARATOR": OP_CODESEPARATOR,
"OP_CHECKSIG": OP_CHECKSIG,
"OP_CHECKSIGVERIFY": OP_CHECKSIGVERIFY,
"OP_CHECKMULTISIG": OP_CHECKMULTISIG,
"OP_CHECKMULTISIGVERIFY": OP_CHECKMULTISIGVERIFY,
"OP_CHECKDATASIG": OP_CHECKDATASIG,
"OP_CHECKDATASIGVERIFY": OP_CHECKDATASIGVERIFY,
"OP_NOP1": OP_NOP1,
"OP_CHECKLOCKTIMEVERIFY": OP_CHECKLOCKTIMEVERIFY,
"OP_CHECKSEQUENCEVERIFY": OP_CHECKSEQUENCEVERIFY,
"OP_NOP4": OP_NOP4,
"OP_NOP5": OP_NOP5,
"OP_NOP6": OP_NOP6,
"OP_NOP7": OP_NOP7,
"OP_NOP8": OP_NOP8,
"OP_NOP9": OP_NOP9,
"OP_NOP10": OP_NOP10,
"OP_PLACE": OP_PLACE,
"OP_PUSH_TX_STATE": OP_PUSH_TX_STATE,
"OP_SETBMD": OP_SETBMD,
"OP_BIN2_BIGNUM": OP_BIN2_BIGNUM,
"OP_EXEC": OP_EXEC,
"OP_GROUP": OP_GROUP,
"OP_TEMPLATE": OP_TEMPLATE,
"OP_SMALLINTEGER": OP_SMALLINTEGER,
"OP_PUBKEYS": OP_PUBKEYS,
"OP_PUBKEYHASH": OP_PUBKEYHASH,
"OP_PUBKEY": OP_PUBKEY,
}
class CScriptInvalidError(Exception):
class CScriptTruncatedPushDataError(CScriptInvalidError):
def __init__(self, msg, data):
self.data = data
super().__init__(msg)
class CScriptNum:
def __init__(self, d=0):
self.value = d
@staticmethod
def encode(obj):
r = bytearray(0)
if obj.value == 0:
return bytes(r)
neg = obj.value < 0
absvalue = -obj.value if neg else obj.value
while absvalue:
r.append(absvalue & 0xFF)
absvalue >>= 8
if r[-1] & 0x80:
r.append(0x80 if neg else 0)
elif neg:
r[-1] |= 0x80
return bytes(bchr(len(r)) + r)
class CScript(bytes):
@classmethod
def __coerce_instance(cls, other):
if isinstance(other, CScriptOp):
other = bchr(other)
elif isinstance(other, CScriptNum):
if other.value == 0:
other = bchr(CScriptOp(OP_0))
else:
other = CScriptNum.encode(other)
elif isinstance(other, int):
if 0 <= other <= 16:
other = bytes(bchr(CScriptOp.encode_op_n(other)))
elif other == -1:
other = bytes(bchr(OP_1_NEGATE))
else:
other = CScriptOp.encode_op_pushdata(bn2vch(other))
elif isinstance(other, (bytes, bytearray)):
other = CScriptOp.encode_op_pushdata(other)
return other
def __add__(self, other):
other = self.__coerce_instance(other)
try:
return CScript(super().__add__(other))
except TypeError as exc:
raise TypeError(
f"Can not add a {other.__class__!r} instance to a CScript"
) from exc
def join(self, _iterable):
raise NotImplementedError
def __new__(cls, value=b""):
if isinstance(value, (bytes, bytearray)):
return super(CScript, cls).__new__(cls, value)
if isinstance(value, str):
return super(CScript, cls).__new__(cls, bytearray.fromhex(value))
def coerce_iterable(iterable):
for instance in iterable:
yield cls.__coerce_instance(instance)
return super(CScript, cls).__new__(cls, b"".join(coerce_iterable(value)))
def raw_iter(self):
i = 0
while i < len(self):
sop_idx = i
opcode = bord(self[i])
i += 1
if opcode > OP_PUSHDATA4:
yield (opcode, None, sop_idx)
else:
datasize = None
pushdata_type = None
if opcode < OP_PUSHDATA1:
pushdata_type = f"PUSHDATA({opcode})"
datasize = opcode
elif opcode == OP_PUSHDATA1:
pushdata_type = "PUSHDATA1"
if i >= len(self):
raise CScriptInvalidError("PUSHDATA1: missing data length")
datasize = bord(self[i])
i += 1
elif opcode == OP_PUSHDATA2:
pushdata_type = "PUSHDATA2"
if i + 1 >= len(self):
raise CScriptInvalidError("PUSHDATA2: missing data length")
datasize = bord(self[i]) + (bord(self[i + 1]) << 8)
i += 2
elif opcode == OP_PUSHDATA4:
pushdata_type = "PUSHDATA4"
if i + 3 >= len(self):
raise CScriptInvalidError("PUSHDATA4: missing data length")
datasize = (
bord(self[i])
+ (bord(self[i + 1]) << 8)
+ (bord(self[i + 2]) << 16)
+ (bord(self[i + 3]) << 24)
)
i += 4
else:
assert False
data = bytes(self[i : i + datasize])
if len(data) < datasize:
raise CScriptTruncatedPushDataError(
f"{pushdata_type}: truncated data", data
)
i += datasize
yield (opcode, data, sop_idx)
def nth(self, n):
cnt = 0
for op in self:
if cnt == n:
return op
cnt += 1
return None
def __iter__(self):
for opcode, data, _sop_idx in self.raw_iter():
if data is not None:
yield data
else:
opcode = CScriptOp(opcode)
if opcode.is_small_int():
yield opcode.decode_op_n()
else:
yield CScriptOp(opcode)
def __repr__(self):
def _repr(o):
if isinstance(o, bytes):
return f"x('{hexlify(o).decode('ascii')}')"
return repr(o)
ops = []
i = iter(self)
while True:
op = None
try:
op = _repr(next(i))
except CScriptTruncatedPushDataError as err:
op = f"{_repr(err.data)}...<ERROR: {err}>"
break
except CScriptInvalidError as err:
op = f"<ERROR: {err}>"
break
except StopIteration:
break
finally:
if op is not None:
ops.append(op)
return f"CScript([{', '.join(ops)}])"
def prettyprint(self, outfile=stdout):
indent = 0
newline = False
for op in iter(self):
if isinstance(op, bytes):
rop = hexlify(op).decode("ascii")
else:
rop = repr(op)
if op in [OP_ELSE, OP_ENDIF, OP_NOTIF, OP_IF] and not newline:
print(file=outfile)
newline = True
if op in [OP_ELSE, OP_ENDIF, OP_NOTIF]:
indent -= 1
if newline:
print(4 * indent * " ", file=outfile, end="")
newline = "VERIFY" in rop or op in [
OP_IF,
OP_ELSE,
OP_ENDIF,
OP_NOTIF,
OP_RETURN,
]
print(rop + " ", file=outfile, end="\n" if newline else "")
if op in [OP_ELSE, OP_IF]:
indent += 1
def get_sig_op_count(self, f_accurate):
n = 0
last_opcode = OP_INVALIDOPCODE
for opcode, _data, _sop_idx in self.raw_iter():
if opcode in (
OP_CHECKSIG,
OP_CHECKSIGVERIFY,
OP_CHECKDATASIG,
OP_CHECKDATASIGVERIFY,
):
n += 1
elif opcode in (OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY):
if f_accurate and (OP_1 <= last_opcode <= OP_16):
n += opcode.decode_op_n()
else:
n += 20
last_opcode = opcode
return n
def find_and_delete(script, sig):
r = b""
last_sop_idx = sop_idx = 0
skip = True
for _opcode, _data, sop_idx in script.raw_iter():
if not skip:
r += script[last_sop_idx:sop_idx]
last_sop_idx = sop_idx
skip = script[sop_idx : sop_idx + len(sig)] == sig
if not skip:
r += script[last_sop_idx:]
return CScript(r)
def test_script_repr():
x = CScript([OP_CHECKDATASIG]) + b"\x11\x22\x33"
assert "112233" in repr(x)
def signature_hash(script, tx_to, in_idx, hashtype, amount):
return tx_to.SignatureHash(
scriptCode=script, in_number=in_idx, nValue=amount, hashcode=hashtype
)