diff --git a/crypto/enums/constants.py b/crypto/enums/constants.py new file mode 100644 index 00000000..53ed6e28 --- /dev/null +++ b/crypto/enums/constants.py @@ -0,0 +1,5 @@ +from enum import Enum + +class Constants(Enum): + ETHEREUM_RECOVERY_ID_OFFSET = 27 + EIP_1559_PREFIX = '02' diff --git a/crypto/identity/private_key.py b/crypto/identity/private_key.py index d15d9ae1..6ff93a8f 100644 --- a/crypto/identity/private_key.py +++ b/crypto/identity/private_key.py @@ -1,8 +1,9 @@ from binascii import hexlify from hashlib import sha256 - from coincurve import PrivateKey as PvtKey +from crypto.enums.constants import Constants + class PrivateKey(object): def __init__(self, private_key: str): self.private_key = PvtKey.from_hex(private_key) @@ -18,8 +19,21 @@ def sign(self, message: bytes) -> bytes: bytes: signature of the signed message """ signature = self.private_key.sign(message) - - return hexlify(signature).decode() + + return hexlify(signature) + + def sign_compact(self, message: bytes) -> bytes: + """Sign a message with this private key object + + Args: + message (bytes): bytes data you want to sign + + Returns: + bytes: signature of the signed message + """ + der = self.private_key.sign_recoverable(message) + + return bytes([der[64] + Constants.ETHEREUM_RECOVERY_ID_OFFSET.value]) + der[0:64] def to_hex(self): """Returns a private key in hex format diff --git a/crypto/transactions/deserializer.py b/crypto/transactions/deserializer.py index 416a5c40..ab21002b 100644 --- a/crypto/transactions/deserializer.py +++ b/crypto/transactions/deserializer.py @@ -1,3 +1,7 @@ +import re +from binascii import unhexlify +from typing import Optional +from crypto.enums.constants import Constants from crypto.transactions.types.abstract_transaction import AbstractTransaction from crypto.transactions.types.transfer import Transfer from crypto.transactions.types.evm_call import EvmCall @@ -5,15 +9,10 @@ from crypto.transactions.types.unvote import Unvote from crypto.transactions.types.validator_registration import ValidatorRegistration from crypto.transactions.types.validator_resignation import ValidatorResignation -from binascii import unhexlify, hexlify -from binary.unsigned_integer.reader import ( - read_bit8, - read_bit32, - read_bit64, -) from crypto.enums.abi_function import AbiFunction from crypto.utils.abi_decoder import AbiDecoder +from crypto.utils.rlp_decoder import RlpDecoder class Deserializer: SIGNATURE_SIZE = 64 @@ -23,77 +22,39 @@ def __init__(self, serialized: str): self.serialized = unhexlify(serialized) if isinstance(serialized, str) else serialized self.pointer = 0 + self.encoded_rlp = '0x' + serialized[2:] + @staticmethod def new(serialized: str): return Deserializer(serialized) def deserialize(self) -> AbstractTransaction: - data = {} + decoded_rlp = RlpDecoder.decode(self.encoded_rlp) + + data = { + 'network': Deserializer.parse_number(decoded_rlp[0]), + 'nonce': Deserializer.parse_big_number(decoded_rlp[1]), + 'gasPrice': Deserializer.parse_number(decoded_rlp[3]), + 'gasLimit': Deserializer.parse_number(decoded_rlp[4]), + 'recipientAddress': Deserializer.parse_address(decoded_rlp[5]), + 'value': Deserializer.parse_big_number(decoded_rlp[6]), + 'data': Deserializer.parse_hex(decoded_rlp[7]), + } + + if len(decoded_rlp) == 12: + data['v'] = Deserializer.parse_number(decoded_rlp[9]) + Constants.ETHEREUM_RECOVERY_ID_OFFSET.value + data['r'] = Deserializer.parse_hex(decoded_rlp[10]) + data['s'] = Deserializer.parse_hex(decoded_rlp[11]) - self.deserialize_common(data) - self.deserialize_data(data) transaction = self.guess_transaction_from_data(data) - self.deserialize_signatures(data) transaction.data = data transaction.recover_sender() - transaction.data['id'] = transaction.hash(skip_signature=False).hex() + transaction.data['id'] = transaction.get_id() return transaction - def read_bytes(self, length: int) -> bytes: - result = self.serialized[self.pointer:self.pointer + length] - self.pointer += length - return result - - def deserialize_common(self, data: dict): - data['network'] = read_bit8(self.serialized, self.pointer) - self.pointer += 1 - - nonce = read_bit64(self.serialized, self.pointer) - data['nonce'] = str(nonce) - self.pointer += 8 - - gas_price = read_bit32(self.serialized, self.pointer) - data['gasPrice'] = gas_price - self.pointer += 4 - - gas_limit = read_bit32(self.serialized, self.pointer) - data['gasLimit'] = gas_limit - self.pointer += 4 - - data['value'] = '0' - - def deserialize_data(self, data: dict): - value = int.from_bytes(self.serialized[self.pointer:self.pointer + 32], byteorder='big') - self.pointer += 32 - - data['value'] = str(value) - - recipient_marker = read_bit8(self.serialized, self.pointer) - self.pointer += 1 - - if recipient_marker == 1: - recipient_address_bytes = self.read_bytes(20) - recipient_address = '0x' + hexlify(recipient_address_bytes).decode() - data['recipientAddress'] = recipient_address - - payload_length = read_bit32(self.serialized, self.pointer) - self.pointer += 4 - - payload_hex = '' - if payload_length > 0: - payload_bytes = self.read_bytes(payload_length) - payload_hex = hexlify(payload_bytes).decode() - - data['data'] = payload_hex - - def deserialize_signatures(self, data: dict): - signature_length = self.SIGNATURE_SIZE + self.RECOVERY_SIZE - signature_bytes = self.read_bytes(signature_length) - data['signature'] = hexlify(signature_bytes).decode() - def guess_transaction_from_data(self, data: dict) -> AbstractTransaction: if data['value'] != '0': return Transfer(data) @@ -115,7 +76,7 @@ def guess_transaction_from_data(self, data: dict) -> AbstractTransaction: else: return EvmCall(data) - def decode_payload(self, data: dict) -> dict: + def decode_payload(self, data: dict) -> Optional[dict]: payload = data.get('data', '') if payload == '': @@ -126,4 +87,21 @@ def decode_payload(self, data: dict) -> dict: return decoder.decode_function_data(payload) except Exception as e: print(f"Error decoding payload: {str(e)}") - return None \ No newline at end of file + + return None + + @staticmethod + def parse_number(value: str) -> int: + return 0 if value == '0x' else int(value, 16) + + @staticmethod + def parse_big_number(value: str) -> str: + return str(Deserializer.parse_number(value)) + + @staticmethod + def parse_hex(value: str) -> str: + return re.sub(r'^0x', '', value) + + @staticmethod + def parse_address(value: str) -> Optional[str]: + return None if value == '0x' else value diff --git a/crypto/transactions/serializer.py b/crypto/transactions/serializer.py index b10d4dbd..97a61577 100644 --- a/crypto/transactions/serializer.py +++ b/crypto/transactions/serializer.py @@ -1,11 +1,5 @@ -from binascii import unhexlify from crypto.transactions.types.abstract_transaction import AbstractTransaction -from crypto.configuration.network import get_network -from binary.unsigned_integer.writer import ( - write_bit8, - write_bit32, - write_bit64, -) +from crypto.utils.transaction_utils import TransactionUtils class Serializer: def __init__(self, transaction: AbstractTransaction): @@ -22,48 +16,6 @@ def get_bytes(transaction: AbstractTransaction, skip_signature: bool = False) -> return transaction.serialize(skip_signature=skip_signature) def serialize(self, skip_signature: bool = False) -> bytes: - bytes_data = bytes() + transaction_hash = TransactionUtils.to_buffer(self.transaction.data, skip_signature=skip_signature).decode() - bytes_data += self.serialize_common() - bytes_data += self.serialize_data() - if not skip_signature: - bytes_data += self.serialize_signatures() - - return bytes_data - - def serialize_common(self) -> bytes: - bytes_data = bytes() - network_version = self.transaction.data.get('network', get_network()['version']) - bytes_data += write_bit8(int(network_version)) - bytes_data += write_bit64(int(self.transaction.data['nonce'])) - bytes_data += write_bit32(int(self.transaction.data['gasPrice'])) - bytes_data += write_bit32(int(self.transaction.data['gasLimit'])) - return bytes_data - - def serialize_data(self) -> bytes: - bytes_data = bytes() - - bytes_data += int(self.transaction.data['value']).to_bytes(32, byteorder='big') - - if 'recipientAddress' in self.transaction.data: - bytes_data += write_bit8(1) - recipient_address = self.transaction.data['recipientAddress'] - bytes_data += unhexlify(recipient_address.replace('0x', '')) - else: - bytes_data += write_bit8(0) - - payload_hex = self.transaction.data.get('data', '') - payload_length = len(payload_hex) // 2 - bytes_data += write_bit32(payload_length) - - if payload_length > 0: - bytes_data += unhexlify(payload_hex) - - return bytes_data - - def serialize_signatures(self) -> bytes: - bytes_data = bytes() - if 'signature' in self.transaction.data: - bytes_data += unhexlify(self.transaction.data['signature']) - - return bytes_data + return bytes.fromhex(transaction_hash) diff --git a/crypto/transactions/types/abstract_transaction.py b/crypto/transactions/types/abstract_transaction.py index d7506152..d66ed034 100644 --- a/crypto/transactions/types/abstract_transaction.py +++ b/crypto/transactions/types/abstract_transaction.py @@ -2,9 +2,10 @@ from typing import Optional from crypto.configuration.network import get_network +from crypto.enums.constants import Constants from crypto.identity.address import address_from_public_key from crypto.identity.private_key import PrivateKey -from crypto.utils.transaction_hasher import TransactionHasher +from crypto.utils.transaction_utils import TransactionUtils from coincurve import PublicKey from crypto.utils.abi_decoder import AbiDecoder @@ -19,7 +20,7 @@ def get_payload(self) -> str: def decode_payload(self, data: dict) -> Optional[dict]: if 'data' not in data or data['data'] == '': return None - + payload = data['data'] decoder = AbiDecoder() @@ -31,51 +32,59 @@ def refresh_payload_data(self): self.data['data'] = self.get_payload().lstrip('0x') def get_id(self) -> str: - return self.hash(skip_signature=False).hex() + return TransactionUtils.get_id(self.data) def get_bytes(self, skip_signature: bool = False) -> bytes: from crypto.transactions.serializer import Serializer + return Serializer.get_bytes(self, skip_signature) def sign(self, private_key: PrivateKey): - hash_ = self.hash(skip_signature=True) - signature_with_recid = private_key.private_key.sign_recoverable(hash_, hasher=None) - signature_hex = signature_with_recid.hex() - self.data['signature'] = signature_hex + transaction_hash = TransactionUtils.to_buffer(self.data, skip_signature=True).decode() + + message = bytes.fromhex(transaction_hash) + + transaction_signature = private_key.sign_compact(message) + + self.data['v'] = transaction_signature[0] + self.data['r'] = transaction_signature[1:33].hex() + self.data['s'] = transaction_signature[33:].hex() + return self def get_public_key(self, compact_signature, hash_): public_key = PublicKey.from_signature_and_message(compact_signature, hash_, hasher=None) + return public_key def recover_sender(self): - signature_hex = self.data.get('signature') - if not signature_hex: - raise ValueError("No signature to recover from") + signature_with_recid = self.get_signature() + if not signature_with_recid: + return False - signature_with_recid = bytes.fromhex(signature_hex) - hash_ = self.hash(skip_signature=True) + hash_ = bytes.fromhex(self.hash(skip_signature=True)) public_key = self.get_public_key(signature_with_recid, hash_) self.data['senderPublicKey'] = public_key.format().hex() self.data['senderAddress'] = address_from_public_key(self.data['senderPublicKey']) def verify(self) -> bool: - signature_hex = self.data.get('signature') - if not signature_hex: + signature_with_recid = self.get_signature() + if not signature_with_recid: return False - signature_with_recid = bytes.fromhex(signature_hex) - hash_ = self.hash(skip_signature=True) + hash_ = bytes.fromhex(self.hash(skip_signature=True)) recovered_public_key = self.get_public_key(signature_with_recid, hash_) sender_public_key_hex = self.data.get('senderPublicKey') if not sender_public_key_hex: return False sender_public_key_bytes = bytes.fromhex(sender_public_key_hex) + return recovered_public_key.format() == sender_public_key_bytes def serialize(self, skip_signature: bool = False) -> bytes: from crypto.transactions.serializer import Serializer + return Serializer(self).serialize(skip_signature) def to_dict(self) -> dict: @@ -84,14 +93,15 @@ def to_dict(self) -> dict: def to_json(self) -> str: return json.dumps(self.to_dict()) - def hash(self, skip_signature: bool) -> bytes: - hash_data = self.data.copy() - if skip_signature: - hash_data['signature'] = None - return TransactionHasher.to_hash(hash_data, skip_signature) + def hash(self, skip_signature: bool) -> str: + return TransactionUtils.to_hash(self.data, skip_signature=skip_signature) def get_signature(self): - signature_hex = self.data.get('signature') - if signature_hex: - return bytes.fromhex(signature_hex) + recover_id = int(self.data.get('v', 0)) - Constants.ETHEREUM_RECOVERY_ID_OFFSET.value + r = self.data.get('r') + s = self.data.get('s') + + if r and s: + return bytes.fromhex(r) + bytes.fromhex(s) + bytes([recover_id]) + return None diff --git a/crypto/utils/rlp_decoder.py b/crypto/utils/rlp_decoder.py new file mode 100644 index 00000000..a42fe729 --- /dev/null +++ b/crypto/utils/rlp_decoder.py @@ -0,0 +1,122 @@ +import re +from typing import TypedDict, Union + +class DecodedType(TypedDict): + consumed: int + result: Union[str, list] + +class RlpDecoder: + @classmethod + def decode(cls, data: str) -> Union[str, list]: + """ + Decode RLP data from a hex string. + """ + bytes_data = cls.__get_bytes(data) + decoded = cls.__decode(bytes_data, 0) + + if decoded['consumed'] != len(bytes_data): + raise ValueError('unexpected junk after RLP payload') + + return decoded['result'] + + @staticmethod + def __get_bytes(value: str) -> list[int]: + if re.match(r'^0x(?:[0-9a-fA-F]{2})*$', value): + hex_value = value[2:] + length = len(hex_value) // 2 + bytes_data = [int(hex_value[i * 2:i * 2 + 2], 16) for i in range(length)] + + return bytes_data + + raise ValueError(f'Invalid BytesLike value: {value}') + + @staticmethod + def __hexlify(data: list[int]) -> str: + return '0x' + ''.join(f'{byte:02x}' for byte in data) + + @staticmethod + def __hexlify_byte(value: int) -> str: + return f'0x{value & 0xff:02x}' + + @staticmethod + def __unarrayify_integer(data: list[int], offset: int, length: int) -> int: + result = 0 + for i in range(length): + result = (result << 8) + data[offset + i] + + return result + + @classmethod + def __decode_children(cls, data: list[int], offset: int, child_offset: int, length: int) -> DecodedType: + result = [] + end = offset + 1 + length + + while child_offset < end: + decoded = cls.__decode(data, child_offset) + result.append(decoded['result']) + child_offset += decoded['consumed'] + + if child_offset > end: + raise ValueError('child data too short or malformed') + + return { + 'consumed': 1 + length, + 'result': result, + } + + @classmethod + def __decode(cls, data: list[int], offset: int) -> DecodedType: + cls.__check_offset(offset, data) + + prefix = data[offset] + + if prefix >= 0xf8: + length_length = prefix - 0xf7 + cls.__check_offset(offset + length_length, data) + + length = cls.__unarrayify_integer(data, offset + 1, length_length) + cls.__check_offset(offset + 1 + length_length + length - 1, data) + + return cls.__decode_children(data, offset, offset + 1 + length_length, length_length + length) + + elif prefix >= 0xc0: + length = prefix - 0xc0 + if length > 0: + cls.__check_offset(offset + 1 + length - 1, data) + + return cls.__decode_children(data, offset, offset + 1, length) + + elif prefix >= 0xb8: + length_length = prefix - 0xb7 + cls.__check_offset(offset + length_length, data) + + length = cls.__unarrayify_integer(data, offset + 1, length_length) + if length > 0: + cls.__check_offset(offset + 1 + length_length + length - 1, data) + slice_data = data[offset + 1 + length_length:offset + 1 + length_length + length] + + return { + 'consumed': 1 + length_length + length, + 'result': cls.__hexlify(slice_data), + } + + elif prefix >= 0x80: + length = prefix - 0x80 + if length > 0: + cls.__check_offset(offset + 1 + length - 1, data) + slice_data = data[offset + 1:offset + 1 + length] + + return { + 'consumed': 1 + length, + 'result': cls.__hexlify(slice_data), + } + + return { + 'consumed': 1, + 'result': cls.__hexlify_byte(prefix), + } + + @classmethod + def __check_offset(cls, offset: int, data: list[int]) -> None: + if offset > len(data): + raise ValueError('data short segment or out of range') diff --git a/crypto/utils/rlp_encoder.py b/crypto/utils/rlp_encoder.py new file mode 100644 index 00000000..e5e63e85 --- /dev/null +++ b/crypto/utils/rlp_encoder.py @@ -0,0 +1,93 @@ +from typing import Union + +class RlpEncoder: + @classmethod + def encode(cls, data: Union[str, bytes, int, list]) -> str: + """ + Encodes the given data into RLP format. + """ + encoded = cls.__encode(data) + hex_str = '' + nibbles = '0123456789abcdef' + + for byte in encoded: + hex_str += nibbles[byte >> 4] + hex_str += nibbles[byte & 0x0f] + + return hex_str + + @classmethod + def __encode(cls, data: Union[str, bytes, int, list]) -> list: + if isinstance(data, list): + payload = [] + for child in data: + payload.extend(cls.__encode(child)) + + payload_length = len(payload) + if payload_length <= 55: + payload.insert(0, 0xc0 + payload_length) + + return payload + + length = cls.__arrayify_integer(payload_length) + length.insert(0, 0xf7 + (len(length))) + + return length + payload + + data = cls.__get_bytes(data) + data_length = len(data) + if data_length == 1 and data[0] <= 0x7f: + return data + + if data_length <= 55: + data.insert(0, 0x80 + data_length) + + return data + + length = cls.__arrayify_integer(len(data)) + length.insert(0, 0xb7 + len(length)) + + return length + data + + @classmethod + def __arrayify_integer(cls, value: int) -> list: + result = [] + while value > 0: + result.insert(0, value & 0xff) + value >>= 8 + + return result + + @classmethod + def __get_bytes(cls, value: Union[str, bytes, int, list]) -> list: + if isinstance(value, str) or isinstance(value, bytes): + if isinstance(value, str): + value = value.encode() + + if value.startswith(b'0x'): + hex_str = value[2:] + if hex_str == '': + return [] + + if len(hex_str) % 2 != 0: + hex_str = b'0' + hex_str + + return [int(hex_str[i:i+2], 16) for i in range(0, len(hex_str), 2)] + + return [char for char in value] + + if isinstance(value, int): + if value == 0: + return [] + + result = [] + while value > 0: + result.insert(0, value & 0xff) + value >>= 8 + + return result + + if isinstance(value, list): + return [v & 0xff for v in value] + + raise ValueError('invalid type', type(value)) diff --git a/crypto/utils/transaction_hasher.py b/crypto/utils/transaction_hasher.py deleted file mode 100644 index 23ba6beb..00000000 --- a/crypto/utils/transaction_hasher.py +++ /dev/null @@ -1,94 +0,0 @@ -import hashlib -from binascii import unhexlify - - -class TransactionHasher: - @staticmethod - def to_hash(transaction: dict, skip_signature: bool = False) -> bytes: - # Process recipientAddress - hex_address = transaction.get('recipientAddress', '').lstrip('0x') - # Pad with leading zero if necessary - if len(hex_address) % 2 != 0: - hex_address = '0' + hex_address - recipient_address = bytes.fromhex(hex_address.lower()) - - # Build the fields array - fields = [ - TransactionHasher.to_be_array(int(transaction['network'])), - TransactionHasher.to_be_array(int(transaction['nonce'])), - TransactionHasher.to_be_array(int(transaction['gasPrice'])), # maxPriorityFeePerGas - TransactionHasher.to_be_array(int(transaction['gasPrice'])), # maxFeePerGas - TransactionHasher.to_be_array(int(transaction['gasLimit'])), - recipient_address, - TransactionHasher.to_be_array(int(transaction['value'])), - bytes.fromhex(transaction.get('data', '').lstrip('0x')) if transaction.get('data') else b'', - [], # Access list is unused - ] - - if not skip_signature and 'signature' in transaction: - signature_buffer = bytes.fromhex(transaction['signature']) - r = signature_buffer[0:32] - s = signature_buffer[32:64] - v = signature_buffer[64] - fields.extend([ - TransactionHasher.to_be_array(v), - r, - s, - ]) - - eip1559_prefix = b'\x02' # Marker for Type 2 (EIP-1559) transaction - - encoded = TransactionHasher.encode_rlp(fields) - hash_input = eip1559_prefix + encoded - - # Use SHA256 for hashing - return hashlib.sha256(hash_input).digest() - - @staticmethod - def to_be_array(value): - if isinstance(value, int): - if value == 0: - return b'' # Empty bytes represent zero - else: - return value.to_bytes((value.bit_length() + 7) // 8, byteorder='big') - elif isinstance(value, bytes): - return value - else: - raise TypeError("Unsupported type for to_be_array") - - @staticmethod - def encode_length(length): - if length == 0: - return b'' - result = [] - while length > 0: - result.insert(0, length & 0xFF) - length >>= 8 - return bytes(result) - - @staticmethod - def encode_rlp(input_data): - if isinstance(input_data, bytes): - input_len = len(input_data) - if input_len == 1 and input_data[0] <= 0x7f: - return input_data - elif input_len <= 55: - return bytes([0x80 + input_len]) + input_data - else: - len_bytes = TransactionHasher.encode_length(input_len) - return bytes([0xb7 + len(len_bytes)]) + len_bytes + input_data - elif isinstance(input_data, list): - output = b''.join([TransactionHasher.encode_rlp(item) for item in input_data]) - output_len = len(output) - if output_len <= 55: - return bytes([0xc0 + output_len]) + output - else: - len_bytes = TransactionHasher.encode_length(output_len) - return bytes([0xf7 + len(len_bytes)]) + len_bytes + output - elif isinstance(input_data, int): - return TransactionHasher.encode_rlp(TransactionHasher.to_be_array(input_data)) - elif input_data is None: - return TransactionHasher.encode_rlp(b'') - else: - # Handle other types by converting to bytes - return TransactionHasher.encode_rlp(str(input_data).encode('utf-8')) diff --git a/crypto/utils/transaction_utils.py b/crypto/utils/transaction_utils.py new file mode 100644 index 00000000..3cfbda5b --- /dev/null +++ b/crypto/utils/transaction_utils.py @@ -0,0 +1,62 @@ +from binascii import unhexlify +import hashlib + +from crypto.enums.constants import Constants +from crypto.utils.rlp_encoder import RlpEncoder + +class TransactionUtils: + @classmethod + def to_buffer(cls, transaction: dict, skip_signature: bool = False) -> bytes: + # Process recipientAddress + hex_address = transaction.get('recipientAddress', '').lstrip('0x') + + # Pad with leading zero if necessary + if len(hex_address) % 2 != 0: + hex_address = '0' + hex_address + + recipient_address = bytes.fromhex(hex_address.lower()) + + # Build the fields array + fields = [ + cls.to_be_array(int(transaction['network'])), + cls.to_be_array(int(transaction.get('nonce', 0))), + cls.to_be_array(0), + cls.to_be_array(int(transaction['gasPrice'])), + cls.to_be_array(int(transaction['gasLimit'])), + recipient_address, + cls.to_be_array(int(transaction.get('value', 0))), + bytes.fromhex(transaction.get('data', '').lstrip('0x')) if transaction.get('data') else b'', + [], + ] + + if not skip_signature and 'v' in transaction and 'r' in transaction and 's' in transaction: + fields.append(cls.to_be_array(int(transaction['v']) - Constants.ETHEREUM_RECOVERY_ID_OFFSET.value)) + fields.append(bytes.fromhex(transaction['r'])) + fields.append(bytes.fromhex(transaction['s'])) + + encoded = RlpEncoder.encode(fields) + + hash_input = Constants.EIP_1559_PREFIX.value + encoded + + return hash_input.encode() + + @classmethod + def to_hash(cls, transaction: dict, skip_signature: bool = False) -> str: + return hashlib.sha256(unhexlify(cls.to_buffer(transaction, skip_signature))).hexdigest() + + @classmethod + def get_id(cls, transaction: dict) -> str: + return cls.to_hash(transaction) + + @staticmethod + def to_be_array(value): + if isinstance(value, int): + if value == 0: + return b'' + else: + return value.to_bytes((value.bit_length() + 7) // 8, byteorder='big') + + if isinstance(value, bytes): + return value + + raise TypeError("Unsupported type for to_be_array") diff --git a/tests/fixtures/evm-sign.json b/tests/fixtures/evm-sign.json index 600ffa2d..a171db39 100644 --- a/tests/fixtures/evm-sign.json +++ b/tests/fixtures/evm-sign.json @@ -1,16 +1,18 @@ { "data": { "network": 30, - "nonce": "13", + "nonce": "1", "gasPrice": 5, "gasLimit": 1000000, "value": "0", - "recipientAddress": "0xE536720791A7DaDBeBdBCD8c8546fb0791a11901", - "data": "a9059cbb00000000000000000000000027fa7caffaae77ddb9ab232fdbda56d5e5af2393000000000000000000000000000000000000000000000000016345785d8a0000", - "signature": "ba30f9042519079895c7408b0e92046c3f20680e0a9294e38ab3cfdd19b26cd4036fe2a80644abb922f1ad7cd682811a83c20120a8030df47b244a3bc44f4dbd00", + "recipientAddress": "0xe536720791a7dadbebdbcd8c8546fb0791a11901", + "data": "a9059cbb00000000000000000000000027fa7caffaae77ddb9ab232fdbda56d5e5af23930000000000000000000000000000000000000000000000000000000000000064", "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", - "id": "3935ff0fe84ea6ac42fc889ed7cda4f97ddd11fd2d1c31e9201f14866acb6edc" + "id": "2cfa9d51e71f014f8054881052e41dc61267587f9dba04c59a31cc881f8ce35b", + "v": 27, + "r": "a56cf78a7203927af0c8216fdbc804182a0788569e460067efec519b6f7b2e55", + "s": "52ce951a20c7406a4a34707557f02e5a2af1451cc4e216645778c4ee0391a4cd" }, - "serialized": "1e0d000000000000000500000040420f00000000000000000000000000000000000000000000000000000000000000000001e536720791a7dadbebdbcd8c8546fb0791a1190144000000a9059cbb00000000000000000000000027fa7caffaae77ddb9ab232fdbda56d5e5af2393000000000000000000000000000000000000000000000000016345785d8a0000ba30f9042519079895c7408b0e92046c3f20680e0a9294e38ab3cfdd19b26cd4036fe2a80644abb922f1ad7cd682811a83c20120a8030df47b244a3bc44f4dbd00" + "serialized": "02f8a81e018005830f424094e536720791a7dadbebdbcd8c8546fb0791a1190180b844a9059cbb00000000000000000000000027fa7caffaae77ddb9ab232fdbda56d5e5af23930000000000000000000000000000000000000000000000000000000000000064c080a0a56cf78a7203927af0c8216fdbc804182a0788569e460067efec519b6f7b2e55a052ce951a20c7406a4a34707557f02e5a2af1451cc4e216645778c4ee0391a4cd" } diff --git a/tests/fixtures/multipayment-empty.json b/tests/fixtures/multipayment-empty.json new file mode 100644 index 00000000..7a235183 --- /dev/null +++ b/tests/fixtures/multipayment-empty.json @@ -0,0 +1,18 @@ +{ + "data": { + "network": 30, + "nonce": "1", + "gasPrice": 5, + "gasLimit": 1000000, + "value": "0", + "recipientAddress": "0x83769BeEB7e5405ef0B7dc3C66C43E3a51A6d27f", + "data": "084ce7080000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", + "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "id": "d816a0f8b51eb618c4497aeddcc9d4596f94fefc39de306282f50596c9e1c071", + "v": 28, + "r": "bfab2a6634cfbf8bc802d0259dea575b7618131e8de9e29ca7df1950e68ce8eb", + "s": "5e7eeb4c2f973a8397f38b68520123798b07b4f1bf251bbaaacafb36e8e05a55" + }, + "serialized": "02f8e81e018005830f42409483769beeb7e5405ef0b7dc3c66c43e3a51a6d27f80b884084ce7080000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c001a0bfab2a6634cfbf8bc802d0259dea575b7618131e8de9e29ca7df1950e68ce8eba05e7eeb4c2f973a8397f38b68520123798b07b4f1bf251bbaaacafb36e8e05a55" +} diff --git a/tests/fixtures/multipayment-single.json b/tests/fixtures/multipayment-single.json new file mode 100644 index 00000000..b2e6098b --- /dev/null +++ b/tests/fixtures/multipayment-single.json @@ -0,0 +1,18 @@ +{ + "data": { + "network": 30, + "nonce": "1", + "gasPrice": 5, + "gasLimit": 1000000, + "value": "100000000", + "recipientAddress": "0x83769BeEB7e5405ef0B7dc3C66C43E3a51A6d27f", + "data": "084ce7080000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008233f6df6449d7655f4643d2e752dc8d2283fad500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000005f5e100", + "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", + "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "id": "d3775117889fce2397bded88564427dda03c31f2b85a839567f98ae776e3ff4c", + "v": 28, + "r": "c00a8f6a658c1dda434bceb708c74d62d086ec143939c2373010284221d25995", + "s": "43f6e7e5988f37c4336638c1b6a0cf4aa6caeefd5bda6937aa03464bdf44536c" + }, + "serialized": "02f9012c1e018005830f42409483769beeb7e5405ef0b7dc3c66c43e3a51a6d27f8405f5e100b8c4084ce7080000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000008233f6df6449d7655f4643d2e752dc8d2283fad500000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000005f5e100c001a0c00a8f6a658c1dda434bceb708c74d62d086ec143939c2373010284221d25995a043f6e7e5988f37c4336638c1b6a0cf4aa6caeefd5bda6937aa03464bdf44536c" +} diff --git a/tests/fixtures/multipayment.json b/tests/fixtures/multipayment.json new file mode 100644 index 00000000..20e781c9 --- /dev/null +++ b/tests/fixtures/multipayment.json @@ -0,0 +1,19 @@ +{ + "data": { + "network": 30, + "nonce": "1", + "gasPrice": 5, + "gasLimit": 1000000, + "valaue": "3000000000000000000", + "value": "300000000", + "recipientAddress": "0x83769BeEB7e5405ef0B7dc3C66C43E3a51A6d27f", + "data": "084ce708000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000008233f6df6449d7655f4643d2e752dc8d2283fad50000000000000000000000008233f6df6449d7655f4643d2e752dc8d2283fad500000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000000bebc200", + "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", + "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "id": "8ef542e888c41642297bf3e4cb0f58cfe3a07e38adcb74cd2f121b5653c4db9e", + "v": 27, + "r": "39e6a6fb8c41c33d1c56c6b0f8c15f977582f9fcf626dbc84b82526db201c8dc", + "s": "285f0849073a3952511bdc34682668272799eacf3cd7556a9800f46d57e557ea" + }, + "serialized": "02f9016d1e018005830f42409483769beeb7e5405ef0b7dc3c66c43e3a51a6d27f8411e1a300b90104084ce708000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000008233f6df6449d7655f4643d2e752dc8d2283fad50000000000000000000000008233f6df6449d7655f4643d2e752dc8d2283fad500000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000000bebc200c080a039e6a6fb8c41c33d1c56c6b0f8c15f977582f9fcf626dbc84b82526db201c8dca0285f0849073a3952511bdc34682668272799eacf3cd7556a9800f46d57e557ea" +} diff --git a/tests/fixtures/transfer-0.json b/tests/fixtures/transfer-0.json new file mode 100644 index 00000000..8d69083d --- /dev/null +++ b/tests/fixtures/transfer-0.json @@ -0,0 +1,18 @@ +{ + "data": { + "network": 30, + "nonce": "123", + "gasPrice": 5, + "gasLimit": 21000, + "recipientAddress": "0xb693449adda7efc015d87944eae8b7c37eb1690a", + "value": "0", + "data": "", + "v": 28, + "r": "f7bae93b75f06c39600f123ca6c805a38636db9643eac29d20d21c6bf0d2eca2", + "s": "17265ff237bff3fe0620c53ff1615c4b8ad9136fe3d61dca38562c60dc5fcae2", + "senderPublicKey": "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd", + "senderAddress": "0x41aD2bc63A2059f9b623533d87fe99887D794847", + "id": "7d6ad61dabbc9907484080100096c9c7c6364c130ca003f9dd730901c112437f" + }, + "serialized": "02f8621e7b800582520894b693449adda7efc015d87944eae8b7c37eb1690a8080c001a0f7bae93b75f06c39600f123ca6c805a38636db9643eac29d20d21c6bf0d2eca2a017265ff237bff3fe0620c53ff1615c4b8ad9136fe3d61dca38562c60dc5fcae2" +} diff --git a/tests/fixtures/transfer-large-amount.json b/tests/fixtures/transfer-large-amount.json new file mode 100644 index 00000000..0fcee465 --- /dev/null +++ b/tests/fixtures/transfer-large-amount.json @@ -0,0 +1,18 @@ +{ + "data": { + "network": 30, + "nonce": "17", + "gasPrice": 5, + "gasLimit": 21000, + "recipientAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "value": "10000000000000000000", + "data": "", + "v": 27, + "r": "c741f8ccf811e7080216b71e0cbcad1138a4f187b08c749c5e8780568f1ff4bc", + "s": "530341bef473fa92db1fe96922758ed010ba8eb80f2e527c9441771fa4c5368b", + "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", + "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "id": "fb9e521867f484cae4b59585d55ac55a99a8c6adfa0cf8b8b50d242115cbb40d" + }, + "serialized": "02f86a1e118005825208946f0182a0cc707b055322ccf6d4cb6a5aff1aeb22888ac7230489e8000080c080a0c741f8ccf811e7080216b71e0cbcad1138a4f187b08c749c5e8780568f1ff4bca0530341bef473fa92db1fe96922758ed010ba8eb80f2e527c9441771fa4c5368b" +} diff --git a/tests/fixtures/transfer.json b/tests/fixtures/transfer.json index 4c577082..24a2b1aa 100644 --- a/tests/fixtures/transfer.json +++ b/tests/fixtures/transfer.json @@ -1,16 +1,18 @@ { "data": { "network": 30, - "nonce": "12", + "nonce": "1", "gasPrice": 5, "gasLimit": 21000, - "value": "10000000000000000000", - "recipientAddress": "0x07Ac3E438719be72a9e2591bB6015F10E8Af2468", + "recipientAddress": "0x6f0182a0cc707b055322ccf6d4cb6a5aff1aeb22", + "value": "100000000", "data": "", - "signature": "b3bc84c8caf1b75c18a78dde87df9f555161003d341eafad659ab672501185e413a26284c3c95056809c7d440c4ffab26179c538864c4d14534ebd5a961852bf01", + "v": 27, + "r": "0567c4def813a66e03fa1cd499a27c6922698a67e25e0b38458d8f4bb0e581fc", + "s": "25fcdf9d110b82bde15bae4a49118deb83cfc3ec1656c2a29286d7836d328abe", "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", - "id": "b5d7b17d30da123d9eebc8bb6012c1a4e950e1dad2b080404bb052c30b8a8b2e" + "id": "0b8a05781cc5ea130573f33330a739f9a17f7d889bcfab519b09ee5ccc7f359d" }, - "serialized": "1e0c0000000000000005000000085200000000000000000000000000000000000000000000000000008ac7230489e800000107ac3e438719be72a9e2591bb6015f10e8af246800000000b3bc84c8caf1b75c18a78dde87df9f555161003d341eafad659ab672501185e413a26284c3c95056809c7d440c4ffab26179c538864c4d14534ebd5a961852bf01" + "serialized": "02f8661e018005825208946f0182a0cc707b055322ccf6d4cb6a5aff1aeb228405f5e10080c080a00567c4def813a66e03fa1cd499a27c6922698a67e25e0b38458d8f4bb0e581fca025fcdf9d110b82bde15bae4a49118deb83cfc3ec1656c2a29286d7836d328abe" } diff --git a/tests/fixtures/unvote.json b/tests/fixtures/unvote.json index d5e3a7cf..5dccb90f 100644 --- a/tests/fixtures/unvote.json +++ b/tests/fixtures/unvote.json @@ -1,16 +1,18 @@ { "data": { - "network": 30, - "nonce": "13", "gasPrice": 5, - "gasLimit": 200000, - "value": "0", - "recipientAddress": "0x522B3294E6d06aA25Ad0f1B8891242E335D3B459", - "data": "3174b689", - "signature": "d7534ec92c06a8547d0f2b3d3259dff5b0b17f8673d68dff9af023009c9c450e24205cb5f4fd6165d71c8b3ba3e9f741d1853110d44bd1e798e87f1a5d6a89c501", + "network": 30, + "id": "4f43cc9b97433bfc86f3aaa55dbedfad1f15c76b527ec68f7a80fc15105d2e43", + "gasLimit": 1000000, + "nonce": "1", "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", - "id": "92ca281a6699a4eb08e8e5c4a644c216026f6c6d3560611c50cab54d1300b690" + "recipientAddress": "0x535b3d7a252fa034ed71f0c53ec0c6f784cb64e1", + "value": "0", + "data": "3174b689", + "v": 27, + "r": "2853135c30a6b131e9270ce1b52999b8d2bdc18b25bef5b1c13ef4ca8a20ce9e", + "s": "3999a9d5d1ff826efa13052394b28b101535c92dcd8744c916c003431e08744e" }, - "serialized": "1e0d0000000000000005000000400d0300000000000000000000000000000000000000000000000000000000000000000001522b3294e6d06aa25ad0f1b8891242e335d3b459040000003174b689d7534ec92c06a8547d0f2b3d3259dff5b0b17f8673d68dff9af023009c9c450e24205cb5f4fd6165d71c8b3ba3e9f741d1853110d44bd1e798e87f1a5d6a89c501" + "serialized": "02f8671e018005830f424094535b3d7a252fa034ed71f0c53ec0c6f784cb64e180843174b689c080a02853135c30a6b131e9270ce1b52999b8d2bdc18b25bef5b1c13ef4ca8a20ce9ea03999a9d5d1ff826efa13052394b28b101535c92dcd8744c916c003431e08744e" } diff --git a/tests/fixtures/username-registration.json b/tests/fixtures/username-registration.json new file mode 100644 index 00000000..46d03a63 --- /dev/null +++ b/tests/fixtures/username-registration.json @@ -0,0 +1,18 @@ +{ + "data": { + "gasPrice": 5, + "network": 30, + "id": "6643b3dec8d2e8aaa188fb83b96c9ebf2ed21d547ff641f267ddc5ef29152f6b", + "gasLimit": 1000000, + "nonce": "1", + "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", + "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", + "recipientAddress": "0x2c1de3b4dbb4adebebb5dcecae825be2a9fc6eb6", + "value": "0", + "data": "36a94134000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000037068700000000000000000000000000000000000000000000000000000000000", + "v": 27, + "r": "48f36028509a88c8670b2baaac14b5ba6a8c88bbd90cce4149a3070e57f0fa42", + "s": "297fe9a4d2df4c682e8705311d1143d2df9c89dc8689853544c1eb96121f5e85" + }, + "serialized": "02f8c81e018005830f4240942c1de3b4dbb4adebebb5dcecae825be2a9fc6eb680b86436a94134000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000037068700000000000000000000000000000000000000000000000000000000000c080a048f36028509a88c8670b2baaac14b5ba6a8c88bbd90cce4149a3070e57f0fa42a0297fe9a4d2df4c682e8705311d1143d2df9c89dc8689853544c1eb96121f5e85" +} diff --git a/tests/fixtures/username-resignation.json b/tests/fixtures/username-resignation.json new file mode 100644 index 00000000..2211eed3 --- /dev/null +++ b/tests/fixtures/username-resignation.json @@ -0,0 +1,17 @@ +{ + "data": { + "gasPrice": 5, + "network": 30, + "id": "b9a6712e63dafc05583d89c0a31a7199d027f06a21369e9df79e2ae2f20dca7e", + "gasLimit": 1000000, + "nonce": "1", + "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", + "recipientAddress": "0x2c1DE3b4Dbb4aDebEbB5dcECAe825bE2a9fc6eb6", + "value": "0", + "data": "ebed6dab", + "v": 28, + "r": "76ade83c279901613b31e41b9cf0e55223ef728edc5d920d108d239929abf523", + "s": "0a8fe944d4217ebfe6afad28f1fce2f03e4cfe7aa90ea932c1dae4216603a82c" + }, + "serialized": "02f8671e018005830f4240942c1de3b4dbb4adebebb5dcecae825be2a9fc6eb68084ebed6dabc001a076ade83c279901613b31e41b9cf0e55223ef728edc5d920d108d239929abf523a00a8fe944d4217ebfe6afad28f1fce2f03e4cfe7aa90ea932c1dae4216603a82c" +} diff --git a/tests/fixtures/validator-registration.json b/tests/fixtures/validator-registration.json index 170909cc..8b276b3d 100644 --- a/tests/fixtures/validator-registration.json +++ b/tests/fixtures/validator-registration.json @@ -1,16 +1,18 @@ { "data": { - "network": 30, - "nonce": "12", "gasPrice": 5, - "gasLimit": 500000, - "value": "0", - "recipientAddress": "0x522B3294E6d06aA25Ad0f1B8891242E335D3B459", - "data": "602a9eee00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030a08058db53e2665c84a40f5152e76dd2b652125a6079130d4c315e728bcf4dd1dfb44ac26e82302331d61977d314111800000000000000000000000000000000", - "signature": "91b2ca61808b94392afa151ee893784a5221ab27b8fdf5871cc17c75e87acca8396530b2f320641326f00199478552e673d124406b44bcbe6075966016658d2201", + "network": 30, + "id": "7d7a6fcda2c5c16347f42f909e3002509c07780aabb7896f42e3172f2f25e92a", + "gasLimit": 1000000, + "nonce": "1", "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", - "id": "3457dfd59d42a174feb30a1aac757e54caddd87d21e6483386a3440cc0fa6c5f" + "recipientAddress": "0x535b3d7a252fa034ed71f0c53ec0c6f784cb64e1", + "value": "0", + "data": "602a9eee00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030954f46d6097a1d314e900e66e11e0dad0a57cd03e04ec99f0dedd1c765dcb11e6d7fa02e22cf40f9ee23d9cc1c0624bd00000000000000000000000000000000", + "v": 27, + "r": "609cfbaf2a8195741dcf2054e73c88e93103d4c13e7f9d5fc83f881c01a38773", + "s": "79266d0b2c9bdaccbc3fba88c6dfad4538bcdcda803b8e0faa33cdd84b73abed" }, - "serialized": "1e0c000000000000000500000020a10700000000000000000000000000000000000000000000000000000000000000000001522b3294e6d06aa25ad0f1b8891242e335d3b45984000000602a9eee00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030a08058db53e2665c84a40f5152e76dd2b652125a6079130d4c315e728bcf4dd1dfb44ac26e82302331d61977d31411180000000000000000000000000000000091b2ca61808b94392afa151ee893784a5221ab27b8fdf5871cc17c75e87acca8396530b2f320641326f00199478552e673d124406b44bcbe6075966016658d2201" + "serialized": "02f8e81e018005830f424094535b3d7a252fa034ed71f0c53ec0c6f784cb64e180b884602a9eee00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030954f46d6097a1d314e900e66e11e0dad0a57cd03e04ec99f0dedd1c765dcb11e6d7fa02e22cf40f9ee23d9cc1c0624bd00000000000000000000000000000000c080a0609cfbaf2a8195741dcf2054e73c88e93103d4c13e7f9d5fc83f881c01a38773a079266d0b2c9bdaccbc3fba88c6dfad4538bcdcda803b8e0faa33cdd84b73abed" } diff --git a/tests/fixtures/validator-resignation.json b/tests/fixtures/validator-resignation.json index 77a37778..5cc76fdd 100644 --- a/tests/fixtures/validator-resignation.json +++ b/tests/fixtures/validator-resignation.json @@ -1,16 +1,18 @@ { "data": { - "network": 30, - "nonce": "12", "gasPrice": 5, - "gasLimit": 150000, - "value": "0", - "recipientAddress": "0x522B3294E6d06aA25Ad0f1B8891242E335D3B459", - "data": "b85f5da2", - "signature": "94fd248dc5984b56be6c9661c5a32fa062fb21af62b1474a33d985302f9bda8a044c30e4feb1f06da437c15d9e997816aa3233b3f142cd780e1ff69b80269d0d00", + "network": 30, + "id": "c4407fe1d819a689c0ad0f7f2a872a15bf26a099865f1d221bddc69f217c4038", + "gasLimit": 1000000, + "nonce": "1", "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", - "id": "ab469546888715725add275778bcf0c1dd68afc163b48018e22a044db718e5b9" + "recipientAddress": "0x535b3d7a252fa034ed71f0c53ec0c6f784cb64e1", + "value": "0", + "data": "b85f5da2", + "v": 27, + "r": "52d016bfb61cb3b57d36a7444ce26d3c557b2acb31a10b5951fc3c9e8d2e49a6", + "s": "4940013218020780485b24cea9afbc6c93249286a308af33735c0c87bdeb4b5a" }, - "serialized": "1e0c0000000000000005000000f0490200000000000000000000000000000000000000000000000000000000000000000001522b3294e6d06aa25ad0f1b8891242e335d3b45904000000b85f5da294fd248dc5984b56be6c9661c5a32fa062fb21af62b1474a33d985302f9bda8a044c30e4feb1f06da437c15d9e997816aa3233b3f142cd780e1ff69b80269d0d00" + "serialized": "02f8671e018005830f424094535b3d7a252fa034ed71f0c53ec0c6f784cb64e18084b85f5da2c080a052d016bfb61cb3b57d36a7444ce26d3c557b2acb31a10b5951fc3c9e8d2e49a6a04940013218020780485b24cea9afbc6c93249286a308af33735c0c87bdeb4b5a" } diff --git a/tests/fixtures/vote.json b/tests/fixtures/vote.json index d7992974..be55d915 100644 --- a/tests/fixtures/vote.json +++ b/tests/fixtures/vote.json @@ -1,16 +1,18 @@ { "data": { - "network": 30, - "nonce": "12", "gasPrice": 5, - "gasLimit": 200000, - "value": "0", - "recipientAddress": "0x522B3294E6d06aA25Ad0f1B8891242E335D3B459", - "data": "6dd7d8ea000000000000000000000000512f366d524157bcf734546eb29a6d687b762255", - "signature": "e1fd7b0ddc466072e2eac37b73283e8303d80ceb2dd2d64a8d6cdf5866662bc5261a08ca2d64942b6bb93b42ed820f1c8c1c92ce2312d380cc83fea022bfc2f301", + "network": 30, + "id": "991a3a63dc47be84d7982acb4c2aae488191373f31b8097e07d3ad95c0997e69", + "gasLimit": 1000000, + "nonce": "1", "senderPublicKey": "023efc1da7f315f3c533a4080e491f32cd4219731cef008976c3876539e1f192d3", "senderAddress": "0x6F0182a0cc707b055322CcF6d4CB6a5Aff1aEb22", - "id": "749744e0d689c46e37ff2993a984599eac4989a9ef0028337b335c9d43abf936" + "recipientAddress": "0x535b3d7a252fa034ed71f0c53ec0c6f784cb64e1", + "value": "0", + "data": "6dd7d8ea000000000000000000000000c3bbe9b1cee1ff85ad72b87414b0e9b7f2366763", + "v": 27, + "r": "1e0b168c7520f39fb99f9bf1ea7c0086a3a9e78f215da5a7e997fd8ef5657f5f", + "s": "35019ef68773310144587b228dbc626e8cf3654377e92901f8d7f13119b0e09e" }, - "serialized": "1e0c0000000000000005000000400d0300000000000000000000000000000000000000000000000000000000000000000001522b3294e6d06aa25ad0f1b8891242e335d3b459240000006dd7d8ea000000000000000000000000512f366d524157bcf734546eb29a6d687b762255e1fd7b0ddc466072e2eac37b73283e8303d80ceb2dd2d64a8d6cdf5866662bc5261a08ca2d64942b6bb93b42ed820f1c8c1c92ce2312d380cc83fea022bfc2f301" + "serialized": "02f8871e018005830f424094535b3d7a252fa034ed71f0c53ec0c6f784cb64e180a46dd7d8ea000000000000000000000000c3bbe9b1cee1ff85ad72b87414b0e9b7f2366763c080a01e0b168c7520f39fb99f9bf1ea7c0086a3a9e78f215da5a7e997fd8ef5657f5fa035019ef68773310144587b228dbc626e8cf3654377e92901f8d7f13119b0e09e" } diff --git a/tests/identity/conftest.py b/tests/identity/conftest.py index 46354bd6..faa90bb6 100644 --- a/tests/identity/conftest.py +++ b/tests/identity/conftest.py @@ -15,6 +15,22 @@ def identity(): } return data +@pytest.fixture +def sign_compact(): + """Identity fixture + """ + data = { + 'data': { + 'serialized': '1b0567c4def813a66e03fa1cd499a27c6922698a67e25e0b38458d8f4bb0e581fc25fcdf9d110b82bde15bae4a49118deb83cfc3ec1656c2a29286d7836d328abe', + 'message': '02e31e018005825208946f0182a0cc707b055322ccf6d4cb6a5aff1aeb228405f5e10080c0', + 'v': 27, + 'r': '0567c4def813a66e03fa1cd499a27c6922698a67e25e0b38458d8f4bb0e581fc', + 's': '25fcdf9d110b82bde15bae4a49118deb83cfc3ec1656c2a29286d7836d328abe', + }, + 'passphrase': 'my super secret passphrase' + } + return data + @pytest.fixture def validator(): """Validator fixture diff --git a/tests/identity/test_private_key.py b/tests/identity/test_private_key.py index 151f1e33..26f181a7 100644 --- a/tests/identity/test_private_key.py +++ b/tests/identity/test_private_key.py @@ -1,6 +1,5 @@ from crypto.identity.private_key import PrivateKey - def test_private_key_from_passphrase(identity): private_key = PrivateKey.from_passphrase(identity['passphrase']) assert isinstance(private_key, PrivateKey) @@ -11,3 +10,14 @@ def test_private_key_from_hex(identity): private_key = PrivateKey.from_hex(identity['data']['private_key']) assert isinstance(private_key, PrivateKey) assert private_key.to_hex() == identity['data']['private_key'] + +def test_sign_compact(sign_compact): + private_key = PrivateKey.from_passphrase(sign_compact['passphrase']) + + message = bytes.fromhex(sign_compact['data']['message']) + signature = private_key.sign_compact(message) + + assert signature[0] == sign_compact['data']['v'] + assert signature[1:33] == bytes.fromhex(sign_compact['data']['r']) + assert signature[33:] == bytes.fromhex(sign_compact['data']['s']) + assert signature.hex() == sign_compact['data']['serialized'] diff --git a/tests/transactions/builder/test_evm_call_builder.py b/tests/transactions/builder/test_evm_call_builder.py index 4ab2e15c..3dfc1635 100644 --- a/tests/transactions/builder/test_evm_call_builder.py +++ b/tests/transactions/builder/test_evm_call_builder.py @@ -5,13 +5,23 @@ def test_evm_call_transaction(passphrase, load_transaction_fixture): builder = ( EvmCallBuilder() - .gas_price(fixture['data']['gasPrice']) - .nonce(fixture['data']['nonce']) - .network(fixture['data']['network']) - .payload(fixture['data']['data']) - .gas_limit(fixture['data']['gasLimit']) - .recipient_address('0xE536720791A7DaDBeBdBCD8c8546fb0791a11901') - .sign(passphrase) + .gas_price(fixture['data']['gasPrice']) + .nonce(fixture['data']['nonce']) + .network(fixture['data']['network']) + .payload(fixture['data']['data']) + .gas_limit(fixture['data']['gasLimit']) + .recipient_address('0xE536720791A7DaDBeBdBCD8c8546fb0791a11901') + .sign(passphrase) ) - assert builder.verify() \ No newline at end of file + assert builder.transaction.data['gasPrice'] == fixture['data']['gasPrice'] + assert builder.transaction.data['nonce'] == fixture['data']['nonce'] + assert builder.transaction.data['network'] == fixture['data']['network'] + assert builder.transaction.data['gasLimit'] == fixture['data']['gasLimit'] + assert builder.transaction.data['recipientAddress'].lower() == fixture['data']['recipientAddress'].lower() + assert builder.transaction.data['value'] == fixture['data']['value'] + assert builder.transaction.data['v'] == fixture['data']['v'] + assert builder.transaction.data['r'] == fixture['data']['r'] + assert builder.transaction.data['s'] == fixture['data']['s'] + + assert builder.verify() diff --git a/tests/transactions/builder/test_transfer_builder.py b/tests/transactions/builder/test_transfer_builder.py index f45cef73..7a053bb7 100644 --- a/tests/transactions/builder/test_transfer_builder.py +++ b/tests/transactions/builder/test_transfer_builder.py @@ -1,19 +1,29 @@ from crypto.transactions.builder.transfer_builder import TransferBuilder -def test_transfer_transaction(passphrase, load_transaction_fixture): +def test_it_should_sign_it_with_a_passphrase(passphrase, load_transaction_fixture): fixture = load_transaction_fixture('transfer') builder = ( TransferBuilder() - .gas_price(fixture['data']['gasPrice']) - .nonce(fixture['data']['nonce']) - .network(fixture['data']['network']) - .gas_limit(fixture['data']['gasLimit']) - .recipient_address(fixture['data']['recipientAddress']) - .value(fixture['data']['value']) - .sign(passphrase) + .gas_price(fixture['data']['gasPrice']) + .nonce(fixture['data']['nonce']) + .network(fixture['data']['network']) + .gas_limit(fixture['data']['gasLimit']) + .recipient_address(fixture['data']['recipientAddress']) + .value(fixture['data']['value']) + .sign(passphrase) ) + assert builder.transaction.data['gasPrice'] == fixture['data']['gasPrice'] + assert builder.transaction.data['nonce'] == fixture['data']['nonce'] + assert builder.transaction.data['network'] == fixture['data']['network'] + assert builder.transaction.data['gasLimit'] == fixture['data']['gasLimit'] + assert builder.transaction.data['recipientAddress'] == fixture['data']['recipientAddress'] + assert builder.transaction.data['value'] == fixture['data']['value'] + assert builder.transaction.data['v'] == fixture['data']['v'] + assert builder.transaction.data['r'] == fixture['data']['r'] + assert builder.transaction.data['s'] == fixture['data']['s'] + assert builder.transaction.serialize().hex() == fixture['serialized'] assert builder.transaction.data['id'] == fixture['data']['id'] assert builder.verify() diff --git a/tests/transactions/builder/test_unvote_builder.py b/tests/transactions/builder/test_unvote_builder.py index 1a286ca8..48896d73 100644 --- a/tests/transactions/builder/test_unvote_builder.py +++ b/tests/transactions/builder/test_unvote_builder.py @@ -5,14 +5,24 @@ def test_unvote_transaction(passphrase, load_transaction_fixture): builder = ( UnvoteBuilder() - .gas_price(fixture['data']['gasPrice']) - .nonce(fixture['data']['nonce']) - .network(fixture['data']['network']) - .gas_limit(fixture['data']['gasLimit']) - .recipient_address(fixture['data']['recipientAddress']) - .sign(passphrase) + .gas_price(fixture['data']['gasPrice']) + .nonce(fixture['data']['nonce']) + .network(fixture['data']['network']) + .gas_limit(fixture['data']['gasLimit']) + .recipient_address(fixture['data']['recipientAddress']) + .sign(passphrase) ) + assert builder.transaction.data['gasPrice'] == fixture['data']['gasPrice'] + assert builder.transaction.data['nonce'] == fixture['data']['nonce'] + assert builder.transaction.data['network'] == fixture['data']['network'] + assert builder.transaction.data['gasLimit'] == fixture['data']['gasLimit'] + assert builder.transaction.data['recipientAddress'] == fixture['data']['recipientAddress'] + assert builder.transaction.data['value'] == fixture['data']['value'] + assert builder.transaction.data['v'] == fixture['data']['v'] + assert builder.transaction.data['r'] == fixture['data']['r'] + assert builder.transaction.data['s'] == fixture['data']['s'] + assert builder.transaction.serialize().hex() == fixture['serialized'] assert builder.transaction.data['id'] == fixture['data']['id'] - assert builder.verify() \ No newline at end of file + assert builder.verify() diff --git a/tests/transactions/builder/test_validator_registration_builder.py b/tests/transactions/builder/test_validator_registration_builder.py index 874cfe22..284cd43a 100644 --- a/tests/transactions/builder/test_validator_registration_builder.py +++ b/tests/transactions/builder/test_validator_registration_builder.py @@ -5,15 +5,25 @@ def test_validator_registration_transaction(passphrase, load_transaction_fixture builder = ( ValidatorRegistrationBuilder() - .gas_price(fixture['data']['gasPrice']) - .nonce(fixture['data']['nonce']) - .network(fixture['data']['network']) - .gas_limit(fixture['data']['gasLimit']) - .validator_public_key('a08058db53e2665c84a40f5152e76dd2b652125a6079130d4c315e728bcf4dd1dfb44ac26e82302331d61977d3141118') - .recipient_address(fixture['data']['recipientAddress']) - .sign(passphrase) + .gas_price(fixture['data']['gasPrice']) + .nonce(fixture['data']['nonce']) + .network(fixture['data']['network']) + .gas_limit(fixture['data']['gasLimit']) + .validator_public_key('954f46d6097a1d314e900e66e11e0dad0a57cd03e04ec99f0dedd1c765dcb11e6d7fa02e22cf40f9ee23d9cc1c0624bd') + .recipient_address(fixture['data']['recipientAddress']) + .sign(passphrase) ) + assert builder.transaction.data['gasPrice'] == fixture['data']['gasPrice'] + assert builder.transaction.data['nonce'] == fixture['data']['nonce'] + assert builder.transaction.data['network'] == fixture['data']['network'] + assert builder.transaction.data['gasLimit'] == fixture['data']['gasLimit'] + assert builder.transaction.data['recipientAddress'] == fixture['data']['recipientAddress'] + assert builder.transaction.data['value'] == fixture['data']['value'] + assert builder.transaction.data['v'] == fixture['data']['v'] + assert builder.transaction.data['r'] == fixture['data']['r'] + assert builder.transaction.data['s'] == fixture['data']['s'] + assert builder.transaction.serialize().hex() == fixture['serialized'] assert builder.transaction.data['id'] == fixture['data']['id'] - assert builder.verify() \ No newline at end of file + assert builder.verify() diff --git a/tests/transactions/builder/test_validator_resignation_builder.py b/tests/transactions/builder/test_validator_resignation_builder.py index e1adfaf1..d51340a2 100644 --- a/tests/transactions/builder/test_validator_resignation_builder.py +++ b/tests/transactions/builder/test_validator_resignation_builder.py @@ -5,14 +5,24 @@ def test_validator_resignation_transaction(passphrase, load_transaction_fixture) builder = ( ValidatorResignationBuilder() - .gas_price(fixture['data']['gasPrice']) - .nonce(fixture['data']['nonce']) - .network(fixture['data']['network']) - .gas_limit(fixture['data']['gasLimit']) - .recipient_address(fixture['data']['recipientAddress']) - .sign(passphrase) + .gas_price(fixture['data']['gasPrice']) + .nonce(fixture['data']['nonce']) + .network(fixture['data']['network']) + .gas_limit(fixture['data']['gasLimit']) + .recipient_address(fixture['data']['recipientAddress']) + .sign(passphrase) ) + assert builder.transaction.data['gasPrice'] == fixture['data']['gasPrice'] + assert builder.transaction.data['nonce'] == fixture['data']['nonce'] + assert builder.transaction.data['network'] == fixture['data']['network'] + assert builder.transaction.data['gasLimit'] == fixture['data']['gasLimit'] + assert builder.transaction.data['recipientAddress'] == fixture['data']['recipientAddress'] + assert builder.transaction.data['value'] == fixture['data']['value'] + assert builder.transaction.data['v'] == fixture['data']['v'] + assert builder.transaction.data['r'] == fixture['data']['r'] + assert builder.transaction.data['s'] == fixture['data']['s'] + assert builder.transaction.serialize().hex() == fixture['serialized'] assert builder.transaction.data['id'] == fixture['data']['id'] - assert builder.verify() \ No newline at end of file + assert builder.verify() diff --git a/tests/transactions/builder/test_vote_builder.py b/tests/transactions/builder/test_vote_builder.py index a8a6f8f9..a94161f3 100644 --- a/tests/transactions/builder/test_vote_builder.py +++ b/tests/transactions/builder/test_vote_builder.py @@ -5,15 +5,25 @@ def test_vote_transaction(passphrase, load_transaction_fixture): builder = ( VoteBuilder() - .gas_price(fixture['data']['gasPrice']) - .nonce(fixture['data']['nonce']) - .network(fixture['data']['network']) - .gas_limit(fixture['data']['gasLimit']) - .recipient_address(fixture['data']['recipientAddress']) - .vote('0x512F366D524157BcF734546eB29a6d687B762255') # Example vote address - .sign(passphrase) + .gas_price(fixture['data']['gasPrice']) + .nonce(fixture['data']['nonce']) + .network(fixture['data']['network']) + .gas_limit(fixture['data']['gasLimit']) + .recipient_address(fixture['data']['recipientAddress']) + .vote('0xC3bBE9B1CeE1ff85Ad72b87414B0E9B7F2366763') # Example vote address + .sign(passphrase) ) + assert builder.transaction.data['gasPrice'] == fixture['data']['gasPrice'] + assert builder.transaction.data['nonce'] == fixture['data']['nonce'] + assert builder.transaction.data['network'] == fixture['data']['network'] + assert builder.transaction.data['gasLimit'] == fixture['data']['gasLimit'] + assert builder.transaction.data['recipientAddress'] == fixture['data']['recipientAddress'] + assert builder.transaction.data['value'] == fixture['data']['value'] + assert builder.transaction.data['v'] == fixture['data']['v'] + assert builder.transaction.data['r'] == fixture['data']['r'] + assert builder.transaction.data['s'] == fixture['data']['s'] + assert builder.transaction.serialize().hex() == fixture['serialized'] assert builder.transaction.data['id'] == fixture['data']['id'] - assert builder.verify() \ No newline at end of file + assert builder.verify() diff --git a/tests/transactions/test_deserializer.py b/tests/transactions/test_deserializer.py index 50357cd2..4c4c44ba 100644 --- a/tests/transactions/test_deserializer.py +++ b/tests/transactions/test_deserializer.py @@ -16,33 +16,57 @@ def assert_deserialized(fixture, keys): def test_deserialize_transfer(load_transaction_fixture): fixture = load_transaction_fixture('transfer') - transaction = assert_deserialized(fixture, ['id', 'nonce', 'gasPrice', 'gasLimit', 'value', 'signature']) - + transaction = assert_deserialized(fixture, ['id', 'nonce', 'gasPrice', 'gasLimit', 'value', 'v', 'r', 's']) + assert isinstance(transaction, Transfer) - assert transaction.data['value'] == '10000000000000000000' + assert transaction.data['value'] == '100000000' def test_deserialize_vote(load_transaction_fixture): fixture = load_transaction_fixture('vote') - transaction = assert_deserialized(fixture, ['id', 'nonce', 'gasPrice', 'gasLimit', 'signature']) - + transaction = assert_deserialized(fixture, ['id', 'nonce', 'gasPrice', 'gasLimit', 'v', 'r', 's']) + assert isinstance(transaction, Vote) - assert transaction.data['vote'] == '0x512F366D524157BcF734546eB29a6d687B762255' - assert transaction.data['id'] == '749744e0d689c46e37ff2993a984599eac4989a9ef0028337b335c9d43abf936' + assert transaction.data['vote'].lower() == '0xc3bbe9b1cee1ff85ad72b87414b0e9b7f2366763' + assert transaction.data['id'] == '991a3a63dc47be84d7982acb4c2aae488191373f31b8097e07d3ad95c0997e69' def test_deserialize_unvote(load_transaction_fixture): fixture = load_transaction_fixture('unvote') - transaction = assert_deserialized(fixture, ['id', 'nonce', 'gasPrice', 'gasLimit', 'signature']) - + transaction = assert_deserialized(fixture, ['id', 'nonce', 'gasPrice', 'gasLimit', 'v', 'r', 's']) + assert isinstance(transaction, Unvote) def test_deserialize_validator_registration(load_transaction_fixture): fixture = load_transaction_fixture('validator-registration') - transaction = assert_deserialized(fixture, ['id', 'nonce', 'gasPrice', 'gasLimit', 'signature']) - + transaction = assert_deserialized(fixture, ['id', 'nonce', 'gasPrice', 'gasLimit', 'v', 'r', 's']) + assert isinstance(transaction, ValidatorRegistration) def test_deserialize_validator_resignation(load_transaction_fixture): fixture = load_transaction_fixture('validator-resignation') - transaction = assert_deserialized(fixture, ['id', 'nonce', 'gasPrice', 'gasLimit', 'signature']) - - assert isinstance(transaction, ValidatorResignation) \ No newline at end of file + transaction = assert_deserialized(fixture, ['id', 'nonce', 'gasPrice', 'gasLimit', 'v', 'r', 's']) + + assert isinstance(transaction, ValidatorResignation) + +def test_parse_number(): + assert Deserializer.parse_number('0x01') == 1 + assert Deserializer.parse_number('0x0100') == 256 + assert Deserializer.parse_number('0x010000') == 65536 + assert Deserializer.parse_number('0x') == 0 + +def test_parse_big_number(): + assert Deserializer.parse_big_number('0x01') == '1' + assert Deserializer.parse_big_number('0x0100') == '256' + assert Deserializer.parse_big_number('0x010000') == '65536' + assert Deserializer.parse_big_number('0x') == '0' + assert Deserializer.parse_big_number('0x52B7D2DCC80CD2E4000000') == '100000000000000000000000000' + +def test_parse_hex(): + assert Deserializer.parse_hex('0x01') == '01' + assert Deserializer.parse_hex('0x0100') == '0100' + assert Deserializer.parse_hex('0x010000') == '010000' + assert Deserializer.parse_hex('0x') == '' + assert Deserializer.parse_hex('0x52B7D2DCC80CD2E4000000') == '52B7D2DCC80CD2E4000000' + +def test_parse_address(): + assert Deserializer.parse_address('0x52B7D2DCC80CD2E4000000') == '0x52B7D2DCC80CD2E4000000' + assert Deserializer.parse_address('0x') == None diff --git a/tests/transactions/test_transaction.py b/tests/transactions/test_transaction.py index 1b316f35..477348ac 100644 --- a/tests/transactions/test_transaction.py +++ b/tests/transactions/test_transaction.py @@ -1,6 +1,5 @@ from crypto.identity.private_key import PrivateKey from crypto.transactions.deserializer import Deserializer -from crypto.transactions.types.abstract_transaction import AbstractTransaction def test_compute_id_of_transaction(load_transaction_fixture): transaction = Deserializer.new(load_transaction_fixture('transfer')['serialized']).deserialize() @@ -10,27 +9,35 @@ def test_sign_transaction_with_passphrase(load_transaction_fixture): private_key = PrivateKey.from_passphrase('this is a top secret passphrase') transaction = Deserializer.new(load_transaction_fixture('transfer')['serialized']).deserialize() - transaction.data['signature'] = '' - - assert 'signature' not in transaction.data or transaction.data['signature'] == '' + transaction.data['v'] = '' + transaction.data['r'] = '' + transaction.data['s'] = '' + + assert 'v' not in transaction.data or transaction.data['v'] == '' + assert 'r' not in transaction.data or transaction.data['r'] == '' + assert 's' not in transaction.data or transaction.data['s'] == '' + transaction.sign(private_key) - assert transaction.data['signature'] != '' + + assert transaction.data['v'] != '' + assert transaction.data['r'] != '' + assert transaction.data['s'] != '' def test_verify_transaction(load_transaction_fixture): - transaction = Deserializer.new(load_transaction_fixture('transfer')['serialized']).deserialize(); + transaction = Deserializer.new(load_transaction_fixture('transfer')['serialized']).deserialize() assert transaction.verify() def test_transaction_to_bytes(load_transaction_fixture): - transaction = Deserializer.new(load_transaction_fixture('transfer')['serialized']).deserialize(); + transaction = Deserializer.new(load_transaction_fixture('transfer')['serialized']).deserialize() actual = transaction.get_bytes() assert isinstance(actual, bytes) def test_transaction_to_array(load_transaction_fixture): - transaction = Deserializer.new(load_transaction_fixture('transfer')['serialized']).deserialize(); + transaction = Deserializer.new(load_transaction_fixture('transfer')['serialized']).deserialize() actual = transaction.to_dict() assert isinstance(actual, dict) def test_transaction_to_json(load_transaction_fixture): - transaction = Deserializer.new(load_transaction_fixture('transfer')['serialized']).deserialize(); + transaction = Deserializer.new(load_transaction_fixture('transfer')['serialized']).deserialize() actual = transaction.to_json() assert isinstance(actual, str) diff --git a/tests/utils/test_rpl_decoder.py b/tests/utils/test_rpl_decoder.py new file mode 100644 index 00000000..e896c426 --- /dev/null +++ b/tests/utils/test_rpl_decoder.py @@ -0,0 +1,41 @@ +from crypto.utils.rlp_decoder import RlpDecoder + + +def test_decode_function_call(load_transaction_fixture): + fixture = load_transaction_fixture('transfer') + + decoded_rlp = RlpDecoder.decode('0x' + fixture['serialized'][2:]) + + assert len(decoded_rlp) == 12 + assert decoded_rlp[0] == '0x1e' + assert decoded_rlp[1] == '0x01' + assert decoded_rlp[2] == '0x' + assert decoded_rlp[3] == '0x05' + assert decoded_rlp[4] == '0x5208' + assert decoded_rlp[5] == '0x6f0182a0cc707b055322ccf6d4cb6a5aff1aeb22' + assert decoded_rlp[6] == '0x05f5e100' + assert decoded_rlp[7] == '0x' + assert decoded_rlp[8] == [] + assert decoded_rlp[9] == '0x' + assert decoded_rlp[10] == '0x0567c4def813a66e03fa1cd499a27c6922698a67e25e0b38458d8f4bb0e581fc' + assert decoded_rlp[11] == '0x25fcdf9d110b82bde15bae4a49118deb83cfc3ec1656c2a29286d7836d328abe' + +def test_decoding_str(): + decoded = RlpDecoder.decode('0x8774657374696e67') + + assert decoded == '0x74657374696e67' + +def test_decoding_bytes(): + decoded = RlpDecoder.decode('0x8774657374696e67') + + assert decoded == '0x74657374696e67' + +def test_decoding_list(): + decoded = RlpDecoder.decode('0xc88774657374696e67') + + assert decoded == ['0x74657374696e67'] + +def test_decoding_int(): + decoded = RlpDecoder.decode('0x86313233343536') + + assert decoded == '0x313233343536' diff --git a/tests/utils/test_rpl_encoder.py b/tests/utils/test_rpl_encoder.py new file mode 100644 index 00000000..ca73437f --- /dev/null +++ b/tests/utils/test_rpl_encoder.py @@ -0,0 +1,31 @@ +from crypto.utils.rlp_encoder import RlpEncoder +from crypto.utils.transaction_utils import TransactionUtils + + +def test_encode_function_call(load_transaction_fixture): + fixture = load_transaction_fixture('transfer') + + # Calls RlpEncoder.encode() with the given transaction + transaction_hash = TransactionUtils.to_buffer(fixture['data']) + + assert transaction_hash.decode() == fixture['serialized'] + +def test_encoding_str(): + encoded = RlpEncoder.encode('testing') + + assert encoded == '8774657374696e67' + +def test_encoding_bytes(): + encoded = RlpEncoder.encode(b'testing') + + assert encoded == '8774657374696e67' + +def test_encoding_list(): + encoded = RlpEncoder.encode(['testing']) + + assert encoded == 'c88774657374696e67' + +def test_encoding_int(): + encoded = RlpEncoder.encode('123456') + + assert encoded == '86313233343536'