def padded_varint_serializations_test() -> None: for _ in range(500): num: int = randint(0, 2**16) var_int: bytes = to_var_int(num) length: int = len(var_int) rpc_var_int: bytes = to_rpc_var_int(num) rpc_length: int = len(rpc_var_int) before: int = randint(0, 32) for i in range(before): var_int = bytes([randint(0, 255)]) + var_int rpc_var_int = bytes([randint(0, 255)]) + rpc_var_int for i in range(randint(0, 32)): var_int += bytes([randint(0, 255)]) rpc_var_int += bytes([randint(0, 255)]) res: Tuple[int, int] = from_var_int(var_int, before) assert res[0] == num assert res[1] == before + length rpc_res: Tuple[int, int] = from_rpc_var_int(rpc_var_int, before) assert res[0] == num assert res[1] == before + length
def __init__(self, tx_hash: bytes, json: Dict[str, Any]) -> None: """Constructor.""" # Hash. self.tx_hash: bytes = tx_hash # Unlock time. self.unlock_time: int = json["unlock_time"] # Parse the inputs. self.inputs: List[AbstractInput] = [] if "gen" in json["vin"][0]: self.inputs.append(MinerInput(json["vin"][0]["gen"]["height"])) else: for i in range(len(json["vin"])): self.inputs.append( Input( json["vin"][i]["key"]["key_offsets"], bytes.fromhex(json["vin"][i]["key"]["k_image"]), )) # Parse the outputs. self.outputs: List[AbstractOutput] = [] for o in range(len(json["vout"])): if "gen" in json["vin"][0]: self.outputs.append( MinerOutput( bytes.fromhex(json["vout"][o]["target"]["key"]), json["vout"][o]["amount"], )) else: self.outputs.append( Output( bytes.fromhex(json["vout"][o]["target"]["key"]), bytes.fromhex( json["rct_signatures"]["ecdhInfo"][o]["amount"]), bytes.fromhex(json["rct_signatures"]["outPk"][o]), )) # Parse extra. self.extra: bytes = bytes(json["extra"]) self.Rs: List[bytes] = [] self.payment_IDs: List[bytes] = [] def skip_tag(cursor: int) -> int: tag_or_length: Tuple[int, int] = from_var_int(self.extra, cursor) cursor = tag_or_length[1] tag_or_length = from_var_int(self.extra, cursor) cursor = tag_or_length[1] + tag_or_length[0] return cursor def check_R(R: bytes) -> bool: if len(R) != 32: return False # This originally also called decodepoint which then calls isoncurve. # It always errored, even with valid Rs. return True try: cursor: int = 0 while cursor < len(self.extra): tag: Tuple[int, int] = from_var_int(self.extra, cursor) cursor = tag[1] # TX_EXTRA_TAG_PADDING if tag[0] == 0x00: cursor += 8 + int.from_bytes(self.extra[cursor:cursor + 8], byteorder="little") # TX_EXTRA_TAG_PUBKEY elif tag[0] == 0x01: potential_R: bytes = self.extra[cursor:cursor + 32] if not check_R(potential_R): break self.Rs.append(potential_R) cursor += 32 # TX_EXTRA_NONCE elif tag[0] == 0x02: length: Tuple[int, int] = from_var_int(self.extra, cursor) cursor = length[1] end: int = cursor + length[0] while cursor + 9 <= end: # Unencrypted payment IDs are a 1-byte header and 32-byte value. # Encrypted payment IDs are a 1-byte header and 8-byte value. # TX_EXTRA_NONCE_PAYMENT_ID if self.extra[cursor] == 0x00: self.payment_IDs.append(self.extra[cursor + 1:cursor + 33]) cursor += 33 continue # TX_EXTRA_NONCE_ENCRYPTED_PAYMENT_ID if self.extra[cursor] == 0x01: self.payment_IDs.append(self.extra[cursor + 1:cursor + 9]) cursor += 9 else: break cursor = end # TX_EXTRA_TAG_ADDITIONAL_PUBKEYS elif tag[0] == 0x04: keys: Tuple[int, int] = from_var_int(self.extra, cursor) cursor = keys[1] for _ in range(keys[0]): potential_R: bytes = self.extra[cursor:cursor + 32] if not check_R(potential_R): break self.Rs.append(potential_R) cursor += 32 # TX_EXTRA_MERGE_MINING_TAG, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG elif (tag[0] == 0x03) or (tag[0] == tag[0] == 0xDE): cursor = skip_tag(cursor) else: break except IndexError: pass # Remove duplicate Rs. # This isn't necessarily secure due to the existence of torsion points, where effectively duplicate Rs can remain. # It must be partnered with a check if we already found an output was spendable. # Since that check would be comprehensive, this is effectively an optimization. self.Rs = list(set(self.Rs)) # Remove duplicate payment IDs. self.payment_IDs = list(set(self.payment_IDs))
def skip_tag(cursor: int) -> int: tag_or_length: Tuple[int, int] = from_var_int(self.extra, cursor) cursor = tag_or_length[1] tag_or_length = from_var_int(self.extra, cursor) cursor = tag_or_length[1] + tag_or_length[0] return cursor
def varint_serializations_test() -> None: for _ in range(500): num: int = randint(0, 2**16) assert from_var_int(to_var_int(num), 0)[0] == num assert from_rpc_var_int(to_rpc_var_int(num), 0)[0] == num