def setUp(self): self.fatd = FATd() config = configparser.ConfigParser() config.read("../test_data/pegnet_config.ini") self.private_key = FactoidPrivateKey(key_string=config['data']['fct_private_key_1']) self.address = self.private_key.get_factoid_address() self.output_addresses = [ FactoidAddress(address_string=config['data']['output_address1']), FactoidAddress(address_string=config['data']['output_address2']), ]
def setup(cls): # These are throwaway private keys. cls.chain_id = "1e5037be95e108c34220d724763444098528e88d08ec30bc15204c98525c3f7d" cls.coinbase_address = "FA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhdF2MC" cls.address1 = FactoidAddress(address_string="FA2gCmih3PaSYRVMt1jLkdG4Xpo2koebUpQ6FpRRnqw5FfTSN2vW") cls._private_fct_key1 = FactoidPrivateKey(key_string="Fs1fR4dMNdyp1a6qYfkLFPJZUyRg1uFW3b6NEK9VaRELD4qALstq") cls.address2 = FactoidAddress(address_string="FA3j68XNwKwvHXV2TKndxPpyCK3KrWTDyyfxzi8LwuM5XRuEmhy6") cls._private_fct_key2 = FactoidPrivateKey(key_string="Fs2jSmXgaysrqiADPmAvvb71NfAa9MqvXvRemozTE8LRc64hLqtf") cls.address3 = FactoidAddress(address_string="FA3rsxWx4WSN5Egj2ZxPoju1mzwfjBivTDMcEvoC1JSsqkddZPCB") cls._private_fct_key3 = FactoidPrivateKey(key_string="Fs2EDKpBA4QQgarTUhJnZeZ4HeymT5U6RSWGsoTtkt1ezGCmNdSo") cls._issuer_signer = ServerIDPrivateKey(key_string="sk12hDMpMzcm9XEdvcy77XwxYU57hpLoCMY1kHtKnyjdGWUpsAvXD")
def add_transfer(self, address: FactoidAddress, amount: int): """ :param address: The public Factoid address receiving the assets being transacted :param amount: The amount of `asset_type` tokens being asked for :return: """ transfer = {"address": address.to_string(), "amount": amount} self.transfers.append(transfer)
def set_input(self, address: FactoidAddress, asset_type: str, amount: int): """ :param address: The public Factoid address spending the assets being transacted :param asset_type: The pegged asset token ticker to transact :param amount: The amount of `asset_type` tokens being transacted :return: """ self.input = {"address": address.to_string(), "type": asset_type, "amount": amount}
def test_key_imports_and_exports(self): private_bytes = b'\0' * 32 private_string = 'Fs1KWJrpLdfucvmYwN2nWrwepLn8ercpMbzXshd1g8zyhKXLVLWj' public_string = 'FA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhdF2MC' private_from_bytes = FactoidPrivateKey(seed_bytes=private_bytes) private_from_string = FactoidPrivateKey(key_string=private_string) assert private_from_bytes.key_bytes == private_bytes assert private_from_string.key_bytes == private_bytes assert private_from_bytes.to_string() == private_string assert private_from_string.to_string() == private_string public_from_private = private_from_string.get_factoid_address() public_from_string = FactoidAddress(address_string=public_string) assert public_from_private.key_bytes is not None assert public_from_string.key_bytes is None assert public_from_private.rcd_hash == public_from_string.rcd_hash assert public_from_private.to_string() == public_string assert public_from_string.to_string() == public_string
def setup(cls): # These are throwaway private keys. cls.chain_id = "145d5207a1ca2978e2a1cb43c97d538cd516d65cd5d14579549664bfecd80296" cls.address1 = FactoidAddress( address_string= "FA2gCmih3PaSYRVMt1jLkdG4Xpo2koebUpQ6FpRRnqw5FfTSN2vW") cls._private_fct_key1 = FactoidPrivateKey( key_string="Fs1fR4dMNdyp1a6qYfkLFPJZUyRg1uFW3b6NEK9VaRELD4qALstq") cls.address2 = FactoidAddress( address_string= "FA3j68XNwKwvHXV2TKndxPpyCK3KrWTDyyfxzi8LwuM5XRuEmhy6") cls._private_fct_key2 = FactoidPrivateKey( key_string="Fs2jSmXgaysrqiADPmAvvb71NfAa9MqvXvRemozTE8LRc64hLqtf") cls.address3 = FactoidAddress( address_string= "FA3rsxWx4WSN5Egj2ZxPoju1mzwfjBivTDMcEvoC1JSsqkddZPCB") cls._private_fct_key3 = FactoidPrivateKey( key_string="Fs2EDKpBA4QQgarTUhJnZeZ4HeymT5U6RSWGsoTtkt1ezGCmNdSo") cls._issue_signer = ServerIDPrivateKey( key_string="sk12hDMpMzcm9XEdvcy77XwxYU57hpLoCMY1kHtKnyjdGWUpsAvXD")
def is_valid(self) -> bool: """ Returns True if the structure of the transaction is sane and able to be executed. Does not take balances or conversion rates into account """ if type(self.input) != dict: return False input_address = self.input.get("address") if not FactoidAddress.is_valid(input_address): return False # Address must be a valid Factoid address string input_type = self.input.get("type") if input_type not in ALL_ASSETS: return False # Input type must be a valid pegged asset input_amount = self.input.get("amount") if type(input_amount) != int or input_amount < 0: return False # Input amount must a positive integer if self.transfers is not None and self.conversion is not None: return False if self.transfers is not None: if type(self.transfers) != list: return False for transfer in self.transfers: if type(transfer) != dict: return False output_address = transfer.get("address") if not FactoidAddress.is_valid(output_address): return False output_amount = transfer.get("amount") if type(output_amount) != int or output_amount < 0: return False # Output amount must be None or a positive integer elif self.conversion not in ALL_ASSETS: return False return True
def validate_address(address: Union[FactoidAddress, str]) -> str: """ Validate a Factoid address and convert to a str. :param address: a Factoid address as a str or a FactoidAddress object """ if isinstance(address, FactoidAddress): address = address.to_string() elif isinstance(address, str): address = FactoidAddress(address_string=address).to_string() else: raise InvalidParam("Invalid address!") return address
def test_key_string_validity_checkers(self): # Valid pair. All zeros private key private = 'Fs1KWJrpLdfucvmYwN2nWrwepLn8ercpMbzXshd1g8zyhKXLVLWj' public = 'FA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhdF2MC' assert FactoidPrivateKey.is_valid(private) assert FactoidAddress.is_valid(public) # Bad prefix private = 'Xs1KWJrpLdfucvmYwN2nWrwepLn8ercpMbzXshd1g8zyhKXLVLWj' public = 'XA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhdF2MC' assert not FactoidAddress.is_valid(private) assert not FactoidAddress.is_valid(public) # Bad body private = 'Fs1KWJrpLdfucvmYwN2nWXXXpLn8ercpMbzXshd1g8zyhKXLVLWj' public = 'FA1zT4aFpEvcnPqPCigB3fXXX4Q4mTXY22iiuV69DqE1pNhdF2MC' assert not FactoidPrivateKey.is_valid(private) assert not FactoidAddress.is_valid(public) # Bad checksums private = 'Fs1KWJrpLdfucvmYwN2nWrwepLn8ercpMbzXshd1g8zyhKXLVLWX' public = 'FA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhdF2MX' assert not FactoidPrivateKey.is_valid(private) assert not FactoidAddress.is_valid(public)
def get_pegnet_balances(self, address: FactoidAddress): """Retrieve all current pegnet balances for the given address""" return self._request("get-pegnet-balances", {"address": address.to_string()})
import fat_py.fat2 as fat2 import fat_py.fat2.consts as consts from factom_keys.fct import FactoidPrivateKey, FactoidAddress from fat_py import FATd fatd = FATd() private_key = FactoidPrivateKey(key_string="Fs3E9gV6DXsYzf7Fqx1fVBQPQXV695eP3k5XbmHEZVRLkMdD9qCK") address = private_key.get_factoid_address() output_addresses = [ FactoidAddress(address_string="FA2ybgFNYQiZFgTjkwQwp74uGsEUHJc6hGEh4YA3ai7FcssemapP"), FactoidAddress(address_string="FA34L6m7rQypr5PVmKGJ1Y4FQ6gDWbVaA49kFTGn1sSVZj6D8pFJ"), ] print(fatd.get_pegnet_balances(address)) tx = fat2.Transaction() tx.set_input(address, "PEG", 1000) tx.add_transfer(output_addresses[0], 500) tx.add_transfer(output_addresses[1], 500) tx_batch = fat2.TransactionBatch() tx_batch.add_transaction(tx) tx_batch.add_signer(private_key) ext_ids, content = tx_batch.sign() print(content.decode()) print(fatd.send_transaction(consts.TRANSACTIONS_CHAIN_ID, ext_ids, content))
def from_entry(cls, external_ids: List[bytes], content: bytes): """ Parses an entry (the external_ids and content) and tries to construct a TransactionEntry. If it does not have the proper structure or all required signatures to cover inputs, None will be returned. """ if len(external_ids) < 3 or len(external_ids) % 2 != 1: return None # Number of external ids = 1 + 2 * N, where N is number of signatures >= 1 timestamp = external_ids[0] # Gather all (public key, signature) pairs from the external ids full_signatures = external_ids[1:] observed_signatures: List[Tuple[FactoidAddress, bytes]] = [] observed_signers: Set[str] = set() for i, rcd in enumerate(full_signatures[::2]): signature = full_signatures[2 * i + 1] if len(rcd) != 33 or len(signature) != 64: return None address_bytes = rcd[1:] address = FactoidAddress(key_bytes=address_bytes) observed_signatures.append((address, signature)) observed_signers.add(address.to_string()) # Check that the content field has a valid json with a "transactions" list try: tx_payload = json.loads(content.decode()) except ValueError: return None if "transactions" not in tx_payload: return None tx_list = tx_payload["transactions"] if type(tx_list) != list: return None # Check that all included inputs are valid and collect the keys we need to have signatures for e = TransactionBatch(timestamp=timestamp.decode()) for tx_dict in tx_list: if type(tx_dict) != dict: return None tx = Transaction( input=tx_dict.get("input"), transfers=tx_dict.get("transfers"), conversion=tx_dict.get("conversion"), metadata=tx_dict.get("metadata"), ) if not tx.is_valid(): return None e.add_transaction(tx) if tx.input["address"] not in observed_signers: return None # Missing this input signer, not a valid entry # Finally check all the signatures chain_id = TRANSACTIONS_CHAIN_ID for i, full_signature in enumerate(observed_signatures): key, signature = full_signature message = bytearray() message.extend(str(i).encode()) message.extend(timestamp) message.extend(chain_id) message.extend(content) message_hash = hashlib.sha512(message).digest() if not key.verify(signature, message_hash): return None return e