diff --git a/examples/query_transactions.py b/examples/query_transactions.py new file mode 100644 index 00000000..449f94e0 --- /dev/null +++ b/examples/query_transactions.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 +# +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + + +# Here are Iroha dependencies. +# Python library generally consists of 3 parts: +# Iroha, IrohaCrypto and IrohaGrpc which we need to import: +import os +import binascii +from iroha import IrohaCrypto +from iroha import Iroha, IrohaGrpc +from google.protobuf.timestamp_pb2 import Timestamp +from iroha.primitive_pb2 import can_set_my_account_detail +import sys + +if sys.version_info[0] < 3: + raise Exception('Python 3 or a more recent version is required.') + +# Here is the information about the environment and admin account information: +IROHA_HOST_ADDR = os.getenv('IROHA_HOST_ADDR', '127.0.0.1') +IROHA_PORT = os.getenv('IROHA_PORT', '50051') +ADMIN_ACCOUNT_ID = os.getenv('ADMIN_ACCOUNT_ID', 'admin@test') +ADMIN_PRIVATE_KEY = os.getenv( + 'ADMIN_PRIVATE_KEY', 'f101537e319568c765b2cc89698325604991dca57b9716b58016b253506cab70') + +# Here we will create user keys +user_private_key = IrohaCrypto.private_key() +user_public_key = IrohaCrypto.derive_public_key(user_private_key) +iroha = Iroha(ADMIN_ACCOUNT_ID) +net = IrohaGrpc('{}:{}'.format(IROHA_HOST_ADDR, IROHA_PORT)) + + +def trace(func): + """ + A decorator for tracing methods' begin/end execution points + """ + + def tracer(*args, **kwargs): + name = func.__name__ + print('\tEntering "{}"'.format(name)) + result = func(*args, **kwargs) + print('\tLeaving "{}"'.format(name)) + return result + + return tracer + +# Let's start defining the commands: +@trace +def send_transaction_and_print_status(transaction): + hex_hash = binascii.hexlify(IrohaCrypto.hash(transaction)) + print('Transaction hash = {}, creator = {}'.format( + hex_hash, transaction.payload.reduced_payload.creator_account_id)) + net.send_tx(transaction) + for status in net.tx_status_stream(transaction): + print(status) + +# For example, below we define a transaction made of 2 commands: +# CreateDomain and CreateAsset. +# Each of Iroha commands has its own set of parameters and there are many commands. +# You can check out all of them here: +# https://iroha.readthedocs.io/en/main/develop/api/commands.html +@trace +def create_domain_and_asset(): + """ + Create domain 'domain' and asset 'coin#domain' with precision 2 + """ + commands = [ + iroha.command('CreateDomain', domain_id='domain', default_role='user'), + iroha.command('CreateAsset', asset_name='coin', + domain_id='domain', precision=2) + ] +# And sign the transaction using the keys from earlier: + tx = IrohaCrypto.sign_transaction( + iroha.transaction(commands), ADMIN_PRIVATE_KEY) + send_transaction_and_print_status(tx) +# You can define queries +# (https://iroha.readthedocs.io/en/main/develop/api/queries.html) +# the same way. + +@trace +def add_coin_to_admin(): + """ + Add 1000.00 units of 'coin#domain' to 'admin@test' + """ + tx = iroha.transaction([ + iroha.command('AddAssetQuantity', + asset_id='coin#domain', amount='1000.00') + ]) + IrohaCrypto.sign_transaction(tx, ADMIN_PRIVATE_KEY) + send_transaction_and_print_status(tx) + tx_tms = tx.payload.reduced_payload.created_time + print(tx_tms) + first_time, last_time = tx_tms - 1, tx_tms + 1 + return first_time, last_time + +@trace +def create_account_userone(): + """ + Create account 'userone@domain' + """ + tx = iroha.transaction([ + iroha.command('CreateAccount', account_name='userone', domain_id='domain', + public_key=user_public_key) + ]) + IrohaCrypto.sign_transaction(tx, ADMIN_PRIVATE_KEY) + send_transaction_and_print_status(tx) + + +@trace +def transfer_coin_from_admin_to_userone(): + """ + Transfer 2.00 'coin#domain' from 'admin@test' to 'userone@domain' + """ + tx = iroha.transaction([ + iroha.command('TransferAsset', src_account_id='admin@test', dest_account_id='userone@domain', + asset_id='coin#domain', description='init top up', amount='2.00') + ]) + IrohaCrypto.sign_transaction(tx, ADMIN_PRIVATE_KEY) + send_transaction_and_print_status(tx) + + +@trace +def userone_grants_to_admin_set_account_detail_permission(): + """ + Make 'admin@test' able to set detail to 'userone@domain' + """ + tx = iroha.transaction([ + iroha.command('GrantPermission', account_id='admin@test', + permission=can_set_my_account_detail) + ], creator_account='userone@domain') + IrohaCrypto.sign_transaction(tx, user_private_key) + send_transaction_and_print_status(tx) + + +@trace +def set_age_to_userone(): + """ + Set age to 'userone@domain' by 'admin@test' + """ + tx = iroha.transaction([ + iroha.command('SetAccountDetail', + account_id='userone@domain', key='age', value='18') + ]) + IrohaCrypto.sign_transaction(tx, ADMIN_PRIVATE_KEY) + send_transaction_and_print_status(tx) + + +@trace +def get_coin_info(): + """ + Get asset info for 'coin#domain' + :return: + """ + query = iroha.query('GetAssetInfo', asset_id='coin#domain') + IrohaCrypto.sign_query(query, ADMIN_PRIVATE_KEY) + + response = net.send_query(query) + data = response.asset_response.asset + print('Asset id = {}, precision = {}'.format(data.asset_id, data.precision)) + + +@trace +def get_account_assets(): + """ + List all the assets of 'userone@domain' + """ + query = iroha.query('GetAccountAssets', account_id='userone@domain') + IrohaCrypto.sign_query(query, ADMIN_PRIVATE_KEY) + + response = net.send_query(query) + data = response.account_assets_response.account_assets + for asset in data: + print('Asset id = {}, balance = {}'.format( + asset.asset_id, asset.balance)) + +@trace +def query_transactions(first_time = None, last_time = None, + first_height = None, last_height = None): + query = iroha.query('GetAccountTransactions', account_id = ADMIN_ACCOUNT_ID, + first_tx_time = first_time, + last_tx_time = last_time, + first_tx_height = first_height, + last_tx_height = last_height, + page_size = 3) + IrohaCrypto.sign_query(query, ADMIN_PRIVATE_KEY) + response = net.send_query(query) + data = response + print(data) + +@trace +def get_userone_details(): + """ + Get all the kv-storage entries for 'userone@domain' + """ + query = iroha.query('GetAccountDetail', account_id='userone@domain') + IrohaCrypto.sign_query(query, ADMIN_PRIVATE_KEY) + + response = net.send_query(query) + data = response.account_detail_response + print('Account id = {}, details = {}'.format('userone@domain', data.detail)) + +# Let's run the commands defined previously: +create_domain_and_asset() +first_time, last_time = add_coin_to_admin() +create_account_userone() +transfer_coin_from_admin_to_userone() +userone_grants_to_admin_set_account_detail_permission() +set_age_to_userone() +get_coin_info() +get_account_assets() +get_userone_details() +# set timestamp to correct value +# for more protobuf timestamp api info see: +# https://googleapis.dev/python/protobuf/latest/google/protobuf/timestamp_pb2.html +first_tx_time = Timestamp() +first_tx_time.FromMilliseconds(first_time) +last_tx_time = Timestamp() +last_tx_time.FromMilliseconds(last_time) +# query for txs in measured time +print('transactions from time interval query: ') +query_transactions(first_tx_time, last_tx_time) +# query for txs in given height range +print('transactions from height range query: ') +query_transactions(first_height = 2, last_height = 3) +print('done') diff --git a/iroha/iroha.py b/iroha/iroha.py index bf772bdb..f757b84b 100644 --- a/iroha/iroha.py +++ b/iroha/iroha.py @@ -194,8 +194,10 @@ def command(name, **kwargs): return command_wrapper def query(self, name, counter=1, creator_account=None, - created_time=None, page_size=None, first_tx_hash=None, - **kwargs): + created_time=None, page_size=None, + first_tx_hash=None, first_tx_time=None, + last_tx_time=None, first_tx_height=None, + last_tx_height=None, **kwargs): """ Creates a protobuf query with specified set of entities :param name: CamelCased name of query to be executed @@ -204,6 +206,10 @@ def query(self, name, counter=1, creator_account=None, :param created_time: query creation timestamp in milliseconds :param page_size: a non-zero positive number, size of result rowset for queries with pagination :param first_tx_hash: optional hash of a transaction that will be the beginning of the next page + :param first_tx_time: optional time of first transaction + :param last_tx_time: optional time of last transaction + :param first_tx_height: optional block height of first transaction + :param last_tx_height: optional block height of last transaction :param kwargs: query arguments as they defined in schema :return: a proto query """ @@ -214,11 +220,19 @@ def query(self, name, counter=1, creator_account=None, created_time = self.now() if not creator_account: creator_account = self.creator_account - if page_size or first_tx_hash: + if page_size or first_tx_hash or first_tx_time or last_tx_time or first_tx_height or last_tx_height: pagination_meta = queries_pb2.TxPaginationMeta() pagination_meta.page_size = page_size if first_tx_hash: pagination_meta.first_tx_hash = first_tx_hash + if first_tx_time != None: + pagination_meta.first_tx_time.CopyFrom(first_tx_time) + if last_tx_time != None: + pagination_meta.last_tx_time.CopyFrom(last_tx_time) + if first_tx_height != None: + pagination_meta.first_tx_height = first_tx_height + if last_tx_height != None: + pagination_meta.last_tx_height = last_tx_height meta = queries_pb2.QueryPayloadMeta() meta.created_time = created_time @@ -235,12 +249,12 @@ def query(self, name, counter=1, creator_account=None, hashes_attr.extend(value) continue setattr(internal_query, key, value) - if pagination_meta: - pagination_meta_attr = getattr(internal_query, 'pagination_meta') - pagination_meta_attr.CopyFrom(pagination_meta) if not len(kwargs): message = getattr(queries_pb2, name)() internal_query.CopyFrom(message) + if pagination_meta: + pagination_meta_attr = getattr(internal_query, 'pagination_meta') + pagination_meta_attr.CopyFrom(pagination_meta) return query_wrapper def blocks_query(self, counter=1, creator_account=None, created_time=None): diff --git a/schema/queries.proto b/schema/queries.proto index 370343c0..dbe484cb 100644 --- a/schema/queries.proto +++ b/schema/queries.proto @@ -7,12 +7,26 @@ syntax = "proto3"; package iroha.protocol; import "primitive.proto"; +import "google/protobuf/timestamp.proto"; message TxPaginationMeta { uint32 page_size = 1; oneof opt_first_tx_hash { string first_tx_hash = 2; } + Ordering ordering = 3; + oneof opt_first_tx_time { + google.protobuf.Timestamp first_tx_time = 4; + } + oneof opt_last_tx_time { + google.protobuf.Timestamp last_tx_time = 5; + } + oneof opt_first_tx_height { + uint64 first_tx_height = 6; + } + oneof opt_last_tx_height { + uint64 last_tx_height = 7; + } } message GetAccount {