Esempio n. 1
0
    def check_degree():
        if request.method == 'GET':
            return render_template('index.html', js_folder=url_for('static', filename='js'),
                                   img_folder=url_for('static', filename='img'),
                                   css_folder=url_for('static', filename='css'),
                                   verif=1)
        horizon = Horizon()
        try:
            # In this part we try to get the hash of the transaction in order to fetch it from horizon. The hash is easier to get from text,
            # that's why it's the first test.
            if 'txhash' in request.form and request.form.get('txhash'):
                tx = horizon.transaction(request.form.get('txhash'))
            elif 'txqr' in request.files:
                # If a qrcode is submitted, we have to save it to decode it, which causes security issues.
                qr = request.files.get('txqr')
                # if user does not select file, browser also
                # submit an empty part without filename
                if qr.filename == '':
                    raise Exception("Vous devez spécifier au moins un hash OU un qrcode")
                else:
                    if app.allowed_filename(qr.filename):
                        fname = secure_filename(qr.filename)
                        fpath = os.path.join(os.getcwd(), 'static', fname)
                        qr.save(fpath)
                        data = qreader.read(fpath)
                        os.remove(fpath) # We don't need it anymore
                        tx = horizon.transaction(data)
                    else:
                        raise Exception("Merci de ne passer que des images générées par ce site")
            else:
                raise Exception("Vous devez spécifier au moins un hash OU un qrcode")

            # Hashing infos from the form to check the hash with the one in the transaction's memo
            name = request.form.get('name')
            birthdate = request.form.get('birthdate')
            year = request.form.get('year')
            h = hash128((name+birthdate+year).encode())
            if h == tx.get('memo'):
                return render_template('index.html', js_folder=url_for('static', filename='js'),
                                       img_folder=url_for('static', filename='img'),
                                       css_folder=url_for('static', filename='css'),
                                       verif=1, verif_passed=1, id=tx.get('source_account'),
                                       name=name, birthdate=birthdate, year=year)
            else:
                return render_template('index.html', js_folder=url_for('static', filename='js'),
                                       img_folder=url_for('static', filename='img'),
                                       css_folder=url_for('static', filename='css'),
                                       verif=1, veriferror="Les informations saisies ne correspondent pas avec l'exemplaire du diplôme décerné")
        except Exception as e:
            return render_template('index.html', js_folder=url_for('static', filename='js'),
                                   img_folder=url_for('static', filename='img'),
                                   css_folder=url_for('static', filename='css'),
                                   verif=1, veriferror=e)
Esempio n. 2
0
class XlmOP:
    def __init__(self, rpc_uri, timeout):
        self.client = Horizon(horizon=rpc_uri, timeout=timeout)  # 测试节点
        self.rpc_uri = rpc_uri
        # self.client = Horizon(horizon=horizon_livenet()) # 正式链

    def generate_pri_keys(self):
        """
        生成随机公钥私钥
        """
        # sm = StellarMnemonic()
        # secret_phrase = sm.generate()
        # kp = Keypair.deterministic(secret_phrase)
        kp = Keypair.random()
        publickey = kp.address().decode()
        seed = kp.seed().decode()
        return seed, publickey

    def create_account(self, old_account_seed, new_account_address, amount,
                       memo):
        """
        用已有账户创建新账户
        """
        horizon = self.client
        account_seed = old_account_seed
        old_account_keypair = Keypair.from_seed(account_seed)
        account_addr = new_account_address
        start_amount = amount  # Your new account minimum balance (in XLM) to transfer over
        # create the CreateAccount operation
        opts = {'destination': account_addr, 'starting_balance': start_amount}
        op = CreateAccount(opts)
        # create a memo
        txt_memo = TextMemo(memo)

        # Get the current sequence of the source account by contacting Horizon. You
        # should also check the response for errors!
        try:
            sequence = horizon.account(
                old_account_keypair.address().decode()).get('sequence')
        except Exception as e:
            logger.exception(e)
        # Create a transaction with our single create account operation, with the
        # default fee of 100 stroops as of this writing (0.00001 XLM)
        trans_ops = {
            'sequence': sequence,
            'memo': txt_memo,
            'operations': [op]
        }
        tx = Transaction(source=old_account_keypair.address().decode(),
                         opts=trans_ops)
        env_opts = {'network_id': 'TESTNET'}
        envelope = TransactionEnvelope(tx=tx, opts=env_opts)
        # Sign the transaction envelope with the source keypair
        envelope.sign(old_account_keypair)

        # Submit the transaction to Horizon
        te_xdr = envelope.xdr()
        response = horizon.submit(te_xdr)
        return response

    def get_balance(self, publickey):
        """
        获取余额
        """
        res_obj = self.client.account(publickey)
        pprint(res_obj)
        # res = json.dumps(res_obj)
        balance = res_obj.get('balances', '')[0].get('balance', '')
        return balance

    def get_account_info(self, address):
        """
        获取账户信息
        """
        account_info = self.client.account(address)
        res = json.dumps(account_info)
        return res

    def create_transaction(self, from_address, to_address, amount, memo):
        """
        创建一笔交易
        :param from_address: 发起账户的私钥
        :param to_address:  目的账户的公钥
        :return: response
        """
        builder = Builder(secret=from_address, horizon_uri=self.rpc_uri)
        builder_memo = builder.add_text_memo(memo)
        builder_memo.append_payment_op(destination=to_address,
                                       amount=amount,
                                       asset_code='XLM')
        builder.sign()
        response = builder.submit()
        return response

    def get_transactions(self, publickey, cursor='now'):
        """
        获取交易列表
        """
        assert publickey and isinstance(publickey, str)
        params = {'order': 'desc', 'limit': '200', 'cursor': cursor}
        trras_res = self.client.account_transactions(publickey, params=params)
        pprint(trras_res)
        return trras_res.get('_embedded').get('records')

    def get_trans_info(self, txid):
        """
        获取交易信息
        """
        trans_res = self.client.transaction(txid)
        return trans_res

    def get_ledgers(self, cursor='now'):
        """
        获取当前区块信息
        """
        param = {'order': 'desc', 'limit': '200', 'cursor': cursor}
        res = self.client.ledgers(**param)
        return res['_embedded']['records']

    def get_account_payments(self, address, cursor='now'):
        param = {
            'order': 'desc',
            'limit': '200',
            'cursor': cursor,
            'address': address
        }
        res = self.client.account_payments(**param)
        return res

    def get_operations(self, address, cursor='now'):

        res_list = []
        for i in range(50):
            params = {
                'order': 'desc',
                'limit': '200',
                'cursor': cursor,
            }
            res = self.client.account_operations(address, params=params)
            pprint(res)
            if res:
                target = res.get('_embedded', '').get('records', '')
                if target:
                    res_list.extend(
                        [i for i in target if i.get('type', '') == 'payment'])
                    last_elm = target[-1]
                    cursor = last_elm['paging_token']
            else:
                break
        return res_list
Esempio n. 3
0
class SDK(object):
    """
    The :class:`~kin.SDK` class is the primary interface to the KIN Python SDK based on Stellar Blockchain.
    It maintains a connection context with a Horizon node and hides all the specifics of dealing with Stellar REST API.
    """

    def __init__(self, base_seed='', horizon_endpoint_uri='', network='PUBLIC', channel_seeds=[]):
        """Create a new instance of the KIN SDK for Stellar.

        If seed is not provided, the SDK can still be used in "anonymous" mode with only the following
        functions available:
            - get_address_lumen_balance
            - get_address_kin_balance
            - check_kin_trusted
            - check_account_exists
            - get_account_data
            - get_transaction_data
            - monitor_address_transactions

        :param str seed: a seed to initialize the sdk wallet account with. If not provided, the wallet will not be
            initialized and methods needing the wallet will raise exception.

        :param str horizon_endpoint_uri: a Horizon endpoint. If not provided, a default endpoint will be used,
            either a testnet or pubnet, depending on the `network` parameter.

        :param str network: either PUBLIC or TESTNET, will set the Horizon endpoint in the absence of
            `horizon_endpoint_uri`.

        :param list channel_seeds: a list of channels to sign transactions with. More channels means less blocking
            on transactions and better response time.

        :return: An instance of the SDK.
        :rtype: :class:`~kin.SDK`

        :raises: :class:`~kin.SdkConfigurationError` if some of the configuration parameters are invalid.
        """

        self.network = network
        if not self.network:
            self.network = 'PUBLIC'

        if horizon_endpoint_uri:
            self.horizon = Horizon(horizon_endpoint_uri)
        else:
            if self.network == 'TESTNET':
                self.horizon = horizon_testnet()
            else:
                self.horizon = horizon_livenet()

        # check Horizon connection
        try:
            self.horizon.query('')
        except Exception as e:
            raise SdkConfigurationError('cannot connect to horizon')

        # init sdk account base_keypair if a base_seed is supplied
        self.base_keypair = None
        if base_seed:
            try:
                validate_seed(base_seed)
            except ValueError:
                raise SdkConfigurationError('invalid base seed: {}'.format(base_seed))
            self.base_keypair = Keypair.from_seed(base_seed)

            # check channel seeds
            if channel_seeds:
                for channel_seed in channel_seeds:
                    try:
                        validate_seed(channel_seed)
                    except ValueError:
                        raise SdkConfigurationError('invalid channel seed: {}'.format(channel_seed))
            else:
                channel_seeds = [base_seed]

            # init channel manager
            self.channel_manager = ChannelManager(base_seed, channel_seeds, self.network, self.horizon.horizon)

    def get_address(self):
        """Get the address of the SDK wallet account.
        The wallet is configured by a seed supplied during SDK initialization.

        :return: public address of the wallet.
        :rtype: str

        :raises: :class:`~kin.SdkConfigurationError`: if the SDK wallet is not configured.
        """
        if not self.base_keypair:
            raise SdkNotConfiguredError('address not configured')
        return self.base_keypair.address().decode()

    def get_lumen_balance(self):
        """Get lumen balance of the SDK wallet.
        The wallet is configured by a seed supplied during SDK initialization.

        :return: : the balance in lumens.
        :rtype: Decimal

        :raises: :class:`~kin.SdkConfigurationError`: if the SDK wallet is not configured.
        """
        return self.get_address_lumen_balance(self.get_address())

    def get_kin_balance(self):
        """Get KIN balance of the SDK wallet.
        The wallet is configured by a seed supplied during SDK initialization.

        :return: : the balance in KIN.
        :rtype: Decimal

        :raises: :class:`~kin.SdkConfigurationError`: if the SDK wallet is not configured.
        """
        return self.get_address_kin_balance(self.get_address())

    def get_address_lumen_balance(self, address):
        """Get lumen balance of the account identified by the provided address.

        :param: str address: the address of the account to query.

        :return: the lumen balance of the account.
        :rtype: Decimal

        :raises: ValueError: if the supplied address has a wrong format.
        """
        return self.get_address_asset_balance(address, Asset.native())

    def get_address_kin_balance(self, address):
        """Get KIN balance of the account identified by the provided address.

        :param: str address: the address of the account to query.

        :return: : the balance in KIN of the account.
        :rtype: Decimal

        :raises: ValueError: if the supplied address has a wrong format.
        """
        return self.get_address_asset_balance(address, KIN_ASSET)

    def get_address_asset_balance(self, address, asset):
        """Get asset balance of the account identified by the provided address.

        :param: str address: the address of the account to query.

        :return: : the balance in asset units of the account.
        :rtype: Decimal

        :raises: ValueError: if the supplied address has a wrong format.
        """
        if not asset.is_native():
            try:
                validate_address(asset.issuer)
            except ValueError:
                raise ValueError('asset issuer invalid')

        acc_data = self.get_account_data(address)
        for balance in acc_data.balances:
            if (balance.asset_type == 'native' and asset.code == 'XLM') \
                    or (balance.asset_code == asset.code and balance.asset_issuer == asset.issuer):
                return balance.balance
        return 0

    def create_account(self, address, starting_balance=MIN_ACCOUNT_BALANCE, memo_text=None):
        """Create a stellar account identified by the provided address.

        :param str address: the address of the account to create.

        :param number starting_balance: the starting balance of the account. If not provided, a default
            MIN_ACCOUNT_BALANCE will be used.

        :param str memo_text: a text to put into transaction memo.

        :return: transaction hash
        :rtype: str

        :raises: :class:`~kin.SdkConfigurationError` if the SDK wallet is not configured.
        :raises: ValueError: if the supplied address has a wrong format.
        """
        if not self.base_keypair:
            raise SdkNotConfiguredError('address not configured')
        validate_address(address)

        return self.channel_manager.send_transaction(lambda builder:
                                                     partial(builder.append_create_account_op, address,
                                                             starting_balance),
                                                     memo_text=memo_text)

    def trust_asset(self, asset, limit=None, memo_text=None):
        """Establish a trustline from the SDK wallet to the asset issuer.

        :param asset: the asset to establish a trustline to.
        :rtype: :class:`~stellar_base.asset.Asset`

        :param number limit: trustline limit.

        :param str memo_text: a text to put into transaction memo.

        :return: transaction hash
        :rtype: str

        :raises: :class:`~kin.SdkConfigurationError` if the SDK wallet is not configured.
        :raises: ValueError: if the issuer address has a wrong format.
        """
        if not self.base_keypair:
            raise SdkNotConfiguredError('address not configured')

        if not asset.is_native():
            try:
                validate_address(asset.issuer)
            except ValueError:
                raise ValueError('asset issuer invalid')

        return self.channel_manager.send_transaction(lambda builder:
                                                     partial(builder.append_trust_op, asset.issuer, asset.code,
                                                             limit=limit),
                                                     memo_text=memo_text)

    def check_kin_trusted(self, address):
        """Check if the account has a trustline to KIN asset.

        :param str address: the account address to query.

        :return: True if the account has a trustline to KIN asset.
        :rtype: boolean

        :raises: ValueError: if the supplied address has a wrong format.
        """
        return self.check_asset_trusted(address, KIN_ASSET)

    def check_asset_trusted(self, address, asset):
        """Check if the account has a trustline to the provided asset.

        :param str address: the account address to query.

        :return: True if the account has a trustline to the asset.
        :rtype: boolean

        :raises: ValueError: if the supplied address has a wrong format.
        :raises: ValueError: if the asset issuer address has a wrong format.
        """
        if not asset.is_native():
            try:
                validate_address(asset.issuer)
            except ValueError:
                raise ValueError('asset issuer invalid')

        acc_data = self.get_account_data(address)
        for balance in acc_data.balances:
            if balance.asset_code == asset.code and balance.asset_issuer == asset.issuer:
                return True
        return False

    def check_account_exists(self, address):
        """Check whether the account identified by the provided address exists.

        :param str address: the account address to query.

        :return: True if the account exists.

        :raises: ValueError: if the supplied address has a wrong format.
        """
        try:
            self.get_account_data(address)
            return True
        except SdkHorizonError as se:
            if se.status == 404:
                return False
            raise

    def send_lumens(self, address, amount, memo_text=None):
        """Send lumens to the account identified by the provided address.

        :param str address: the account to send lumens to.

        :param number amount: the number of lumens to send.

        :param str memo_text: a text to put into transaction memo.

        :return: transaction hash
        :rtype: str

        :raises: :class:`~kin.SdkConfigurationError` if the SDK wallet is not configured.
        :raises: ValueError: if the provided address has a wrong format.
        :raises: ValueError: if the amount is not positive.
        """
        return self.send_asset(address, Asset.native(), amount, memo_text)

    def send_kin(self, address, amount, memo_text=None):
        """Send KIN to the account identified by the provided address.

        :param str address: the account to send KIN to.

        :param number amount: the number of KIN to send.

        :param str memo_text: a text to put into transaction memo.

        :return: transaction hash
        :rtype: str

        :raises: :class:`~kin.SdkConfigurationError` if the SDK wallet is not configured.
        :raises: ValueError: if the provided address has a wrong format.
        :raises: ValueError: if the amount is not positive.
        """
        return self.send_asset(address, KIN_ASSET, amount, memo_text)

    def send_asset(self, address, asset, amount, memo_text=None):
        """Send asset to the account identified by the provided address.

        :param str address: the account to send asset to.

        :param number amount: the asset amount to send.

        :param str memo_text: a text to put into transaction memo.

        :return: transaction hash
        :rtype: str

        :raises: :class:`~kin.SdkConfigurationError` if the SDK wallet is not configured.
        :raises: ValueError: if the provided address has a wrong format.
        :raises: ValueError: if the asset issuer address has a wrong format.
        :raises: ValueError: if the amount is not positive.
        """
        if not self.base_keypair:
            raise SdkNotConfiguredError('address not configured')
        validate_address(address)

        if amount <= 0:
            raise ValueError('amount must be positive')

        if not asset.is_native():
            try:
                validate_address(asset.issuer)
            except ValueError:
                raise ValueError('asset issuer invalid')

        return self.channel_manager.send_transaction(lambda builder:
                                                     partial(builder.append_payment_op, address, amount,
                                                             asset_type=asset.code, asset_issuer=asset.issuer),
                                                     memo_text=memo_text)

    def get_account_data(self, address):
        """Gets account data.

        :param str address: the account to query.

        :return: account data
        :rtype: :class:`~kin.AccountData`

        :raises: ValueError: if the provided address has a wrong format.
        """
        validate_address(address)
        acc = self.horizon.account(address)
        check_horizon_reply(acc)

        return AccountData(acc, strict=False)

    def get_transaction_data(self, tx_hash):
        """Gets transaction data.

        :param str tx_hash: transaction hash.

        :return: transaction data
        :rtype: :class:`~kin.TransactionData`
        """
        # get transaction data
        tx = self.horizon.transaction(tx_hash)
        check_horizon_reply(tx)

        # get transaction operations
        tx_ops = self.horizon.transaction_operations(tx['hash'])  # TODO: max 50, paged?
        check_horizon_reply(tx_ops)

        tx['operations'] = tx_ops['_embedded']['records']

        return TransactionData(tx, strict=False)

    def monitor_transactions(self, callback_fn):
        """Monitor transactions related to the SDK wallet account.
        NOTE: the functions starts a background thread.

        :param callback_fn: the function to call on each received transaction. The function has one parameter,
            which is a transaction data object.
        """
        if not self.base_keypair:
            raise SdkNotConfiguredError('address not configured')
        self.monitor_address_transactions(self.get_address(), callback_fn)

    def monitor_address_transactions(self, address, callback_fn):
        """Monitor transactions related to the account identified by a provided address.
        NOTE: the functions starts a background thread.

        :param callback_fn: the function to call on each received transaction. The function has one parameter,
            which is a transaction data object.

        :raises: ValueError: if the provided address has a wrong format.
        """
        validate_address(address)

        # make the SSE request synchronous (will throw errors in the current thread)
        events = self.horizon.account_transactions(address, sse=True)  # TODO: last_id support
        # check_horizon_reply(events)  # NOTE: not a Horizon reply!  # TODO: why not consistent

        # asynchronous event processor
        def event_processor():
            import json
            for event in events:
                if event.event == 'message':
                    try:
                        tx = json.loads(event.data)

                        # get transaction operations
                        tx_ops = self.horizon.transaction_operations(tx['hash'])  # TODO: max 50, paged?
                        check_horizon_reply(tx_ops)

                        tx['operations'] = tx_ops['_embedded']['records']

                        tx_data = TransactionData(tx, strict=False)
                        callback_fn(tx_data)

                    except Exception as e:
                        logger.exception(e)
                        continue

        # start monitoring thread
        import threading
        t = threading.Thread(target=event_processor)
        t.daemon = True
        t.start()