Beispiel #1
0
    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']),
        ]
Beispiel #2
0
 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")
Beispiel #3
0
 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)
Beispiel #4
0
 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}
Beispiel #5
0
    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
Beispiel #6
0
 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")
Beispiel #7
0
    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
Beispiel #8
0
    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
Beispiel #9
0
    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)
Beispiel #10
0
 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()})
Beispiel #11
0
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))
Beispiel #12
0
    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