Changeset View
Changeset View
Standalone View
Standalone View
test/functional/test_framework/script.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2015-2016 The Bitcoin Core developers | # Copyright (c) 2015-2016 The Bitcoin Core developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Functionality to build scripts, as well as SignatureHash(). | """Functionality to build scripts, as well as SignatureHash(). | ||||
This file is modified from python-bitcoinlib. | This file is modified from python-bitcoinlib. | ||||
""" | """ | ||||
from .bignum import bn2vch | from .bignum import bn2vch | ||||
from binascii import hexlify | from binascii import hexlify | ||||
import hashlib | import hashlib | ||||
import struct | import struct | ||||
import sys | |||||
from .messages import ( | from .messages import ( | ||||
CTransaction, | CTransaction, | ||||
CTxOut, | CTxOut, | ||||
hash256, | hash256, | ||||
ser_string, | ser_string, | ||||
ser_uint256, | ser_uint256, | ||||
sha256, | sha256, | ||||
uint256_from_str, | uint256_from_str, | ||||
) | ) | ||||
bchr = chr | |||||
bord = ord | |||||
if sys.version > '3': | |||||
long = int | |||||
def bchr(x): return bytes([x]) | |||||
def bord(x): return x | |||||
MAX_SCRIPT_ELEMENT_SIZE = 520 | MAX_SCRIPT_ELEMENT_SIZE = 520 | ||||
OPCODE_NAMES = {} | OPCODE_NAMES = {} | ||||
def hash160(s): | def hash160(s): | ||||
return hashlib.new('ripemd160', sha256(s)).digest() | return hashlib.new('ripemd160', sha256(s)).digest() | ||||
_opcode_instances = [] | _opcode_instances = [] | ||||
class CScriptOp(int): | class CScriptOp(int): | ||||
"""A single script opcode""" | """A single script opcode""" | ||||
__slots__ = () | __slots__ = () | ||||
@staticmethod | @staticmethod | ||||
def encode_op_pushdata(d): | def encode_op_pushdata(d): | ||||
"""Encode a PUSHDATA op, returning bytes""" | """Encode a PUSHDATA op, returning bytes""" | ||||
if len(d) < 0x4c: | if len(d) < 0x4c: | ||||
return b'' + bchr(len(d)) + d # OP_PUSHDATA | # OP_PUSHDATA | ||||
return b'' + bytes([len(d)]) + d | |||||
elif len(d) <= 0xff: | elif len(d) <= 0xff: | ||||
return b'\x4c' + bchr(len(d)) + d # OP_PUSHDATA1 | # OP_PUSHDATA1 | ||||
return b'\x4c' + bytes([len(d)]) + d | |||||
elif len(d) <= 0xffff: | elif len(d) <= 0xffff: | ||||
return b'\x4d' + struct.pack(b'<H', len(d)) + d # OP_PUSHDATA2 | return b'\x4d' + struct.pack(b'<H', len(d)) + d # OP_PUSHDATA2 | ||||
elif len(d) <= 0xffffffff: | elif len(d) <= 0xffffffff: | ||||
return b'\x4e' + struct.pack(b'<I', len(d)) + d # OP_PUSHDATA4 | return b'\x4e' + struct.pack(b'<I', len(d)) + d # OP_PUSHDATA4 | ||||
else: | else: | ||||
raise ValueError("Data too long to encode in a PUSHDATA op") | raise ValueError("Data too long to encode in a PUSHDATA op") | ||||
@staticmethod | @staticmethod | ||||
▲ Show 20 Lines • Show All 347 Lines • ▼ Show 20 Lines | def encode(obj): | ||||
absvalue = -obj.value if neg else obj.value | absvalue = -obj.value if neg else obj.value | ||||
while (absvalue): | while (absvalue): | ||||
r.append(absvalue & 0xff) | r.append(absvalue & 0xff) | ||||
absvalue >>= 8 | absvalue >>= 8 | ||||
if r[-1] & 0x80: | if r[-1] & 0x80: | ||||
r.append(0x80 if neg else 0) | r.append(0x80 if neg else 0) | ||||
elif neg: | elif neg: | ||||
r[-1] |= 0x80 | r[-1] |= 0x80 | ||||
return bytes(bchr(len(r)) + r) | return bytes(bytes([len(r)]) + r) | ||||
class CScript(bytes): | class CScript(bytes): | ||||
"""Serialized script | """Serialized script | ||||
A bytes subclass, so you can use this directly whenever bytes are accepted. | A bytes subclass, so you can use this directly whenever bytes are accepted. | ||||
Note that this means that indexing does *not* work - you'll get an index by | Note that this means that indexing does *not* work - you'll get an index by | ||||
byte rather than opcode. This format was chosen for efficiency so that the | byte rather than opcode. This format was chosen for efficiency so that the | ||||
general case would not require creating a lot of little CScriptOP objects. | general case would not require creating a lot of little CScriptOP objects. | ||||
iter(script) however does iterate by opcode. | iter(script) however does iterate by opcode. | ||||
""" | """ | ||||
__slots__ = () | __slots__ = () | ||||
@classmethod | @classmethod | ||||
def __coerce_instance(cls, other): | def __coerce_instance(cls, other): | ||||
# Coerce other into bytes | # Coerce other into bytes | ||||
if isinstance(other, CScriptOp): | if isinstance(other, CScriptOp): | ||||
other = bchr(other) | other = bytes([other]) | ||||
elif isinstance(other, CScriptNum): | elif isinstance(other, CScriptNum): | ||||
if (other.value == 0): | if (other.value == 0): | ||||
other = bchr(CScriptOp(OP_0)) | other = bytes([CScriptOp(OP_0)]) | ||||
else: | else: | ||||
other = CScriptNum.encode(other) | other = CScriptNum.encode(other) | ||||
elif isinstance(other, int): | elif isinstance(other, int): | ||||
if 0 <= other <= 16: | if 0 <= other <= 16: | ||||
other = bytes(bchr(CScriptOp.encode_op_n(other))) | other = bytes([CScriptOp.encode_op_n(other)]) | ||||
elif other == -1: | elif other == -1: | ||||
other = bytes(bchr(OP_1NEGATE)) | other = bytes([OP_1NEGATE]) | ||||
else: | else: | ||||
other = CScriptOp.encode_op_pushdata(bn2vch(other)) | other = CScriptOp.encode_op_pushdata(bn2vch(other)) | ||||
elif isinstance(other, (bytes, bytearray)): | elif isinstance(other, (bytes, bytearray)): | ||||
other = CScriptOp.encode_op_pushdata(other) | other = CScriptOp.encode_op_pushdata(other) | ||||
return other | return other | ||||
def __add__(self, other): | def __add__(self, other): | ||||
# Do the coercion outside of the try block so that errors in it are | # Do the coercion outside of the try block so that errors in it are | ||||
Show All 27 Lines | def raw_iter(self): | ||||
Yields tuples of (opcode, data, sop_idx) so that the different possible | Yields tuples of (opcode, data, sop_idx) so that the different possible | ||||
PUSHDATA encodings can be accurately distinguished, as well as | PUSHDATA encodings can be accurately distinguished, as well as | ||||
determining the exact opcode byte indexes. (sop_idx) | determining the exact opcode byte indexes. (sop_idx) | ||||
""" | """ | ||||
i = 0 | i = 0 | ||||
while i < len(self): | while i < len(self): | ||||
sop_idx = i | sop_idx = i | ||||
opcode = bord(self[i]) | opcode = self[i] | ||||
i += 1 | i += 1 | ||||
if opcode > OP_PUSHDATA4: | if opcode > OP_PUSHDATA4: | ||||
yield (opcode, None, sop_idx) | yield (opcode, None, sop_idx) | ||||
else: | else: | ||||
datasize = None | datasize = None | ||||
pushdata_type = None | pushdata_type = None | ||||
if opcode < OP_PUSHDATA1: | if opcode < OP_PUSHDATA1: | ||||
pushdata_type = 'PUSHDATA({})'.format(opcode) | pushdata_type = 'PUSHDATA({})'.format(opcode) | ||||
datasize = opcode | datasize = opcode | ||||
elif opcode == OP_PUSHDATA1: | elif opcode == OP_PUSHDATA1: | ||||
pushdata_type = 'PUSHDATA1' | pushdata_type = 'PUSHDATA1' | ||||
if i >= len(self): | if i >= len(self): | ||||
raise CScriptInvalidError( | raise CScriptInvalidError( | ||||
'PUSHDATA1: missing data length') | 'PUSHDATA1: missing data length') | ||||
datasize = bord(self[i]) | datasize = self[i] | ||||
i += 1 | i += 1 | ||||
elif opcode == OP_PUSHDATA2: | elif opcode == OP_PUSHDATA2: | ||||
pushdata_type = 'PUSHDATA2' | pushdata_type = 'PUSHDATA2' | ||||
if i + 1 >= len(self): | if i + 1 >= len(self): | ||||
raise CScriptInvalidError( | raise CScriptInvalidError( | ||||
'PUSHDATA2: missing data length') | 'PUSHDATA2: missing data length') | ||||
datasize = bord(self[i]) + (bord(self[i + 1]) << 8) | datasize = self[i] + (self[i + 1] << 8) | ||||
i += 2 | i += 2 | ||||
elif opcode == OP_PUSHDATA4: | elif opcode == OP_PUSHDATA4: | ||||
pushdata_type = 'PUSHDATA4' | pushdata_type = 'PUSHDATA4' | ||||
if i + 3 >= len(self): | if i + 3 >= len(self): | ||||
raise CScriptInvalidError( | raise CScriptInvalidError( | ||||
'PUSHDATA4: missing data length') | 'PUSHDATA4: missing data length') | ||||
datasize = bord(self[i]) + (bord(self[i + 1]) << 8) + \ | datasize = self[i] + (self[i + 1] << 8) + \ | ||||
(bord(self[i + 2]) << 16) + (bord(self[i + 3]) << 24) | (self[i + 2] << 16) + (self[i + 3] << 24) | ||||
i += 4 | i += 4 | ||||
else: | else: | ||||
assert False # shouldn't happen | assert False # shouldn't happen | ||||
data = bytes(self[i:i + datasize]) | data = bytes(self[i:i + datasize]) | ||||
# Check for truncation | # Check for truncation | ||||
▲ Show 20 Lines • Show All 196 Lines • Show Last 20 Lines |