def unmarshal_with_remainder(cls, raw: bytes): """Returns a new FactoidTransaction object, unmarshalling given bytes according to: https://github.com/FactomProject/FactomDocs/blob/master/factomDataStructureDetails.md#factoid-transaction Because FactoidTransaction is variable length, it is useful to pass a "stream" of bytes to be unmarshalled. This way, we don't have to know the size in advance. Just return the remainder bytes for the caller to use elsewhere. """ data = raw[1:] # skip single byte version, probably just 0x02 anyways timestamp, data = int.from_bytes(data[:6], "big", signed=False), data[6:] input_count, data = ord(data[:1]), data[1:] output_count, data = ord(data[:1]), data[1:] ec_purchase_count, data = ord(data[:1]), data[1:] inputs = [] for i in range(input_count): value, data = varint.decode(data) fct_address, data = data[:32], data[32:] inputs.append({"value": value, "fct_address": fct_address}) outputs = [] for i in range(output_count): value, data = varint.decode(data) fct_address, data = data[:32], data[32:] outputs.append({"value": value, "fct_address": fct_address}) ec_purchases = [] for i in range(ec_purchase_count): value, data = varint.decode(data) ec_public_key, data = data[:32], data[32:] ec_purchases.append({ "value": value, "ec_public_key": ec_public_key }) rcds = primitives.FullSignatureList() for i in range(input_count): data = data[1:] # skip 1 byte version number, always 0x01 for now signature, data = primitives.FullSignature.unmarshal( data[:96]), data[96:] rcds.append(signature) return ( FactoidTransaction( timestamp=timestamp, inputs=inputs, outputs=outputs, ec_purchases=ec_purchases, rcds=rcds, ), data, )
def unmarshal_with_remainder(cls, raw: bytes): """ Unmarshal the byte representation into a new object and return the remaining bytes :param raw: marshalled bytes of the message :return: a tuple of (new CoinbaseDescriptorCancel message object, remaining bytes) """ message_size, data = varint.decode(raw) message_data, data = data[:message_size], data[message_size:] descriptor_height, message_data = varint.decode(message_data) descriptor_index, message_data = message_data[:32], message_data[32:] assert len(message_data) == 0, "Extra bytes remaining in message data!" return CoinbaseDescriptorCancel(descriptor_height, descriptor_index), data
def unmarshal_with_remainder(cls, raw: bytes): chain_id, data = raw[:32], raw[32:] assert chain_id == EntryCreditBlockHeader.CHAIN_ID body_hash, data = data[:32], data[32:] prev_header_hash, data = data[:32], data[32:] prev_full_hash, data = data[:32], data[32:] height, data = struct.unpack(">I", data[:4])[0], data[4:] header_expansion_size, data = varint.decode(data) header_expansion_area, data = ( data[:header_expansion_size], data[header_expansion_size:], ) object_count, data = struct.unpack(">Q", data[:8])[0], data[8:] body_size, data = struct.unpack(">Q", data[:8])[0], data[8:] return ( EntryCreditBlockHeader( body_hash=body_hash, prev_header_hash=prev_header_hash, prev_full_hash=prev_full_hash, height=height, expansion_area=header_expansion_area, object_count=object_count, body_size=body_size, ), data, )
def unmarshal_with_remainder(cls, raw: bytes): """ Unmarshal the byte representation into a new object and return the remaining bytes :param raw: marshalled bytes of the message :return: a tuple of (new CoinbaseDescriptor message object, remaining bytes) """ message_size, data = varint.decode(raw) message_data, data = data[:message_size], data[message_size:] outputs = [] while len(message_data) > 0: value, message_data = varint.decode(message_data) fct_address, message_data = message_data[:32], message_data[32:] outputs.append({"value": value, "fct_address": fct_address}) assert len(message_data) == 0, "Extra bytes remaining in message data!" return CoinbaseDescriptor(outputs), data
def unmarshal_with_remainder(cls, raw: bytes): chain_id, data = raw[:32], raw[32:] assert chain_id == FactoidBlockHeader.CHAIN_ID body_mr, data = data[:32], data[32:] prev_keymr, data = data[:32], data[32:] prev_ledger_keymr, data = data[:32], data[32:] ec_exchange_rate, data = struct.unpack(">Q", data[:8])[0], data[8:] height, data = struct.unpack(">I", data[:4])[0], data[4:] header_expansion_size, data = varint.decode(data) header_expansion_area, data = ( data[:header_expansion_size], data[header_expansion_size:], ) tx_count, data = struct.unpack(">I", data[:4])[0], data[4:] body_size, data = struct.unpack(">I", data[:4])[0], data[4:] return ( FactoidBlockHeader( body_mr=body_mr, prev_keymr=prev_keymr, prev_ledger_keymr=prev_ledger_keymr, ec_exchange_rate=ec_exchange_rate, height=height, expansion_area=header_expansion_area, tx_count=tx_count, body_size=body_size, ), data, )
def unmarshal_with_remainder(cls, raw: bytes): """Returns a new BalanceIncrease object along with any remaining data, unmarshalling given bytes according to: https://github.com/FactomProject/FactomDocs/blob/master/factomDataStructureDetails.md#balance-increase Because BalanceIncrease is variable length, it is useful to pass a "stream" of bytes to be unmarshalled. This way, we don't have to know the size in advance. Just return the remainder bytes for the caller to use elsewhere. """ ec_public_key, data = raw[:32], raw[32:] tx_id, data = data[:32], data[32:] index, data = varint.decode(data) quantity, data = varint.decode(data) return ( BalanceIncrease(ec_public_key=ec_public_key, tx_id=tx_id, index=index, quantity=quantity), data, )
def unmarshal_with_remainder(cls, raw: bytes): chain_id, data = raw[:32], raw[32:] assert chain_id == AdminBlockHeader.CHAIN_ID prev_back_reference_hash, data = data[:32], data[32:] height, data = struct.unpack(">I", data[:4])[0], data[4:] expansion_size, data = varint.decode(data) expansion_area, data = data[:expansion_size], data[expansion_size:] # TODO: unmarshal header expansion area message_count, data = struct.unpack(">I", data[:4])[0], data[4:] body_size, data = struct.unpack(">I", data[:4])[0], data[4:] return ( AdminBlockHeader( prev_back_reference_hash=prev_back_reference_hash, height=height, expansion_area=expansion_area, message_count=message_count, body_size=body_size, ), data, )
def unmarshal(cls, raw: bytes): msg_type, data = raw[0], raw[1:] if msg_type != cls.TYPE: raise ValueError("Invalid message type ({})".format(msg_type)) vm_index, data = data[0], data[1:] timestamp, data = data[:6], data[6:] salt, data = data[:8], data[8:] salt_number, data = struct.unpack(">I", data[:4])[0], data[4:] message_hash, data = data[:32], data[32:] full_message_hash, data = data[:32], data[32:] leader_chain_id, data = data[:32], data[32:] height, data = struct.unpack(">I", data[:4])[0], data[4:] process_list_height, data = struct.unpack(">I", data[:4])[0], data[4:] minute, data = data[0], data[1:] serial_hash, data = data[:32], data[32:] data_area_size, data = varint.decode(data) data_area, data = data[:data_area_size], data[data_area_size:] # TODO: unmarshal the data area in Ack message signature, data = primitives.FullSignature.unmarshal( data[:96]), data[96:] return Ack( vm_index=vm_index, timestamp=timestamp, salt=salt, salt_number=salt_number, message_hash=message_hash, full_message_hash=full_message_hash, leader_chain_id=leader_chain_id, height=height, process_list_height=process_list_height, minute=minute, serial_hash=serial_hash, data_area=data_area, signature=signature, )
def test_decode(self): for expected_int, value_varint in TestVarInt.mapping.items(): observed_int, remainder = varint.decode(value_varint) assert observed_int == expected_int, "{} != {}".format( observed_int, expected_int) assert len(remainder) == 0