mirror of
https://github.com/aljazceru/python-teos.git
synced 2025-12-17 22:24:23 +01:00
The simulator has been updated to work with real transaction structures instead of transaction hashes. It now supports: - Non-SegWit transaction format - Generation of blocks event-wise and time-wise Some small issues have also been fixed. With the new approach, the simulator can be used in a broader range of tests. Moreover tests can run faster since they do not have to wait for blocks. Instead, the generation of new blocks can be triggered by the test.
129 lines
4.1 KiB
Python
129 lines
4.1 KiB
Python
# Porting some functionality from https://github.com/sr-gi/bitcoin_tools with some modifications <3
|
|
from hashlib import sha256
|
|
from binascii import unhexlify
|
|
|
|
|
|
def change_endianness(x):
|
|
""" Changes the endianness (from BE to LE and vice versa) of a given value.
|
|
:param x: Given value which endianness will be changed.
|
|
:type x: hex str
|
|
:return: The opposite endianness representation of the given value.
|
|
:rtype: hex str
|
|
"""
|
|
|
|
# If there is an odd number of elements, we make it even by adding a 0
|
|
if (len(x) % 2) == 1:
|
|
x += "0"
|
|
|
|
y = bytes(x, 'utf-8')
|
|
z = y[::-1]
|
|
return z.decode('utf-8')
|
|
|
|
|
|
def parse_varint(tx):
|
|
""" Parses a given transaction for extracting an encoded varint element.
|
|
:param tx: Transaction where the element will be extracted.
|
|
:type tx: TX
|
|
:return: The b-bytes representation of the given value (a) in hex format.
|
|
:rtype: hex str
|
|
"""
|
|
|
|
# First of all, the offset of the hex transaction if moved to the proper position (i.e where the varint should be
|
|
# located) and the length and format of the data to be analyzed is checked.
|
|
data = tx.hex[tx.offset:]
|
|
assert (len(data) > 0)
|
|
size = int(data[:2], 16)
|
|
assert (size <= 255)
|
|
|
|
# Then, the integer is encoded as a varint using the proper prefix, if needed.
|
|
if size <= 252: # No prefix
|
|
storage_length = 1
|
|
elif size == 253: # 0xFD
|
|
storage_length = 3
|
|
elif size == 254: # 0xFE
|
|
storage_length = 5
|
|
elif size == 255: # 0xFF
|
|
storage_length = 9
|
|
else:
|
|
raise Exception("Wrong input data size")
|
|
|
|
# Finally, the storage length is used to extract the proper number of bytes from the transaction hex and the
|
|
# transaction offset is updated.
|
|
varint = data[:storage_length * 2]
|
|
tx.offset += storage_length * 2
|
|
|
|
return varint
|
|
|
|
|
|
def parse_element(tx, size):
|
|
""" Parses a given transaction to extract an element of a given size.
|
|
:param tx: Transaction where the element will be extracted.
|
|
:type tx: TX
|
|
:param size: Size of the parameter to be extracted.
|
|
:type size: int
|
|
:return: The extracted element.
|
|
:rtype: hex str
|
|
"""
|
|
|
|
element = tx.hex[tx.offset:tx.offset + size * 2]
|
|
tx.offset += size * 2
|
|
return element
|
|
|
|
|
|
def encode_varint(value):
|
|
""" Encodes a given integer value to a varint. It only used the four varint representation cases used by bitcoin:
|
|
1-byte, 2-byte, 4-byte or 8-byte integers.
|
|
:param value: The integer value that will be encoded into varint.
|
|
:type value: int
|
|
:return: The varint representation of the given integer value.
|
|
:rtype: str
|
|
"""
|
|
|
|
# The value is checked in order to choose the size of its final representation.
|
|
# 0xFD(253), 0xFE(254) and 0xFF(255) are special cases, since are the prefixes defined for 2-byte, 4-byte
|
|
# and 8-byte long values respectively.
|
|
if value < pow(2, 8) - 3:
|
|
size = 1
|
|
varint = int2bytes(value, size) # No prefix
|
|
else:
|
|
if value < pow(2, 16):
|
|
size = 2
|
|
prefix = 253 # 0xFD
|
|
elif value < pow(2, 32):
|
|
size = 4
|
|
prefix = 254 # 0xFE
|
|
elif value < pow(2, 64):
|
|
size = 8
|
|
prefix = 255 # 0xFF
|
|
else:
|
|
raise Exception("Wrong input data size")
|
|
varint = format(prefix, 'x') + change_endianness(int2bytes(value, size))
|
|
|
|
return varint
|
|
|
|
|
|
def int2bytes(a, b):
|
|
""" Converts a given integer value (a) its b-byte representation, in hex format.
|
|
:param a: Value to be converted.
|
|
:type a: int
|
|
:param b: Byte size to be filled.
|
|
:type b: int
|
|
:return: The b-bytes representation of the given value (a) in hex format.
|
|
:rtype: hex str
|
|
"""
|
|
|
|
m = pow(2, 8*b) - 1
|
|
if a > m:
|
|
raise Exception(str(a) + " is too big to be represented with " + str(b) + " bytes. Maximum value is "
|
|
+ str(m) + ".")
|
|
|
|
return ('%0' + str(2 * b) + 'x') % a
|
|
|
|
|
|
def sha256d(hex_data):
|
|
data = unhexlify(hex_data)
|
|
double_sha256 = sha256(sha256(data).digest()).hexdigest()
|
|
|
|
return change_endianness(double_sha256)
|
|
|