From 9fca2d9b37af16864aa7acd9a66925263a08bfcd Mon Sep 17 00:00:00 2001 From: learnforpractice Date: Tue, 7 Jun 2022 12:35:52 +0800 Subject: [PATCH] Fix running out of transaction index issue --- pysrc/chainapi_async.py | 105 +++++++++++++-------------- pysrc/chainapi_sync.py | 103 ++++++++++++++------------- pysrc/chainnative.py | 42 ++++++----- pysrc/eosBase.py | 153 ++++++++++++++++++++-------------------- pysrc/transaction.py | 8 +++ pytest/test_eosapi.py | 8 +++ src/pyeoskit | 2 +- 7 files changed, 222 insertions(+), 199 deletions(-) diff --git a/pysrc/chainapi_async.py b/pysrc/chainapi_async.py index 28f202ae..c2e5d9be 100644 --- a/pysrc/chainapi_async.py +++ b/pysrc/chainapi_async.py @@ -89,57 +89,60 @@ async def generate_packed_transaction(self, actions, expiration, ref_block, chai else: expiration = int(time.time()) + expiration - tx = Transaction(expiration, ref_block, chain_id) - for a in actions: - contract, action_name, args, permissions = a - if isinstance(args, bytes): - args = args.hex() - elif isinstance(args, dict): - args = json.dumps(args) - elif isinstance(args, str): - pass - else: - tx.free() - raise Exception('Invalid args type') - - if isinstance(permissions, dict): - _permissions = permissions - permissions = [] - for actor in _permissions: - permissions.append({actor: _permissions[actor]}) - permissions = json.dumps(permissions) - self.check_abi(contract) - tx.add_action(contract, action_name, args, permissions) - - local_wallet_pub_keys = wallet.get_public_keys() - available_pub_keys = set(local_wallet_pub_keys) - - ledger_pub_keys = set() - if not indexes is None: - ledger_pub_keys = ledger.get_public_keys(indexes) - available_pub_keys |= set(ledger_pub_keys) - - required_keys = await self.get_sign_keys(fake_actions, list(available_pub_keys)) - required_keys = set(required_keys) - - signatures = set() - sign_keys = required_keys & set(local_wallet_pub_keys) - for key in sign_keys: - signatures.add(tx.sign(key)) - - packed_tx = tx.pack(compress, False) - sign_keys = required_keys & set(ledger_pub_keys) - if not sign_keys: - return packed_tx - - packed_tx = json.loads(packed_tx) - tx_json = tx.json() - for key in sign_keys: - index = indexes[ledger_pub_keys.index(key)] - signs = ledger.sign(tx_json, [index], chain_id) - signatures |= set(signs) - packed_tx['signatures'] = list(signatures) - return json.dumps(packed_tx) + try: + tx = Transaction(expiration, ref_block, chain_id) + for a in actions: + contract, action_name, args, permissions = a + if isinstance(args, bytes): + args = args.hex() + elif isinstance(args, dict): + args = json.dumps(args) + elif isinstance(args, str): + pass + else: + tx.free() + raise Exception('Invalid args type') + + if isinstance(permissions, dict): + _permissions = permissions + permissions = [] + for actor in _permissions: + permissions.append({actor: _permissions[actor]}) + permissions = json.dumps(permissions) + self.check_abi(contract) + tx.add_action(contract, action_name, args, permissions) + + local_wallet_pub_keys = wallet.get_public_keys() + available_pub_keys = set(local_wallet_pub_keys) + + ledger_pub_keys = set() + if not indexes is None: + ledger_pub_keys = ledger.get_public_keys(indexes) + available_pub_keys |= set(ledger_pub_keys) + + required_keys = await self.get_sign_keys(fake_actions, list(available_pub_keys)) + required_keys = set(required_keys) + + signatures = set() + sign_keys = required_keys & set(local_wallet_pub_keys) + for key in sign_keys: + signatures.add(tx.sign(key)) + + packed_tx = tx.pack(compress, False) + sign_keys = required_keys & set(ledger_pub_keys) + if not sign_keys: + return packed_tx + + packed_tx = json.loads(packed_tx) + tx_json = tx.json() + for key in sign_keys: + index = indexes[ledger_pub_keys.index(key)] + signs = ledger.sign(tx_json, [index], chain_id) + signatures |= set(signs) + packed_tx['signatures'] = list(signatures) + return json.dumps(packed_tx) + finally: + tx.free() async def push_action(self, contract, action, args, permissions=None, compress=False, expiration=0, ref_block_id=None, indexes=None, payer=None, payer_permission="active"): if not permissions: diff --git a/pysrc/chainapi_sync.py b/pysrc/chainapi_sync.py index 01f749ec..d960298b 100644 --- a/pysrc/chainapi_sync.py +++ b/pysrc/chainapi_sync.py @@ -89,56 +89,59 @@ def generate_packed_transaction(self, actions, expiration, ref_block, chain_id, else: expiration = int(time.time()) + expiration - tx = Transaction(expiration, ref_block, chain_id) - for a in actions: - contract, action_name, args, permissions = a - if isinstance(args, bytes): - args = args.hex() - elif isinstance(args, dict): - args = json.dumps(args) - elif isinstance(args, str): - pass - else: - tx.free() - raise Exception('Invalid args type') - if isinstance(permissions, dict): - _permissions = permissions - permissions = [] - for actor in _permissions: - permissions.append({actor: _permissions[actor]}) - permissions = json.dumps(permissions) - self.check_abi(contract) - tx.add_action(contract, action_name, args, permissions) - - local_wallet_pub_keys = wallet.get_public_keys() - available_pub_keys = set(local_wallet_pub_keys) - - ledger_pub_keys = set() - if not indexes is None: - ledger_pub_keys = ledger.get_public_keys(indexes) - available_pub_keys |= set(ledger_pub_keys) - - required_keys = self.get_sign_keys(fake_actions, list(available_pub_keys)) - required_keys = set(required_keys) - - signatures = set() - sign_keys = required_keys & set(local_wallet_pub_keys) - for key in sign_keys: - signatures.add(tx.sign(key)) - - packed_tx = tx.pack(compress, False) - sign_keys = required_keys & set(ledger_pub_keys) - if not sign_keys: - return packed_tx - - packed_tx = json.loads(packed_tx) - tx_json = tx.json() - for key in sign_keys: - index = indexes[ledger_pub_keys.index(key)] - signs = ledger.sign(tx_json, [index], chain_id) - signatures |= set(signs) - packed_tx['signatures'] = list(signatures) - return json.dumps(packed_tx) + try: + tx = Transaction(expiration, ref_block, chain_id) + for a in actions: + contract, action_name, args, permissions = a + if isinstance(args, bytes): + args = args.hex() + elif isinstance(args, dict): + args = json.dumps(args) + elif isinstance(args, str): + pass + else: + tx.free() + raise Exception('Invalid args type') + if isinstance(permissions, dict): + _permissions = permissions + permissions = [] + for actor in _permissions: + permissions.append({actor: _permissions[actor]}) + permissions = json.dumps(permissions) + self.check_abi(contract) + tx.add_action(contract, action_name, args, permissions) + + local_wallet_pub_keys = wallet.get_public_keys() + available_pub_keys = set(local_wallet_pub_keys) + + ledger_pub_keys = set() + if not indexes is None: + ledger_pub_keys = ledger.get_public_keys(indexes) + available_pub_keys |= set(ledger_pub_keys) + + required_keys = self.get_sign_keys(fake_actions, list(available_pub_keys)) + required_keys = set(required_keys) + + signatures = set() + sign_keys = required_keys & set(local_wallet_pub_keys) + for key in sign_keys: + signatures.add(tx.sign(key)) + + packed_tx = tx.pack(compress, False) + sign_keys = required_keys & set(ledger_pub_keys) + if not sign_keys: + return packed_tx + + packed_tx = json.loads(packed_tx) + tx_json = tx.json() + for key in sign_keys: + index = indexes[ledger_pub_keys.index(key)] + signs = ledger.sign(tx_json, [index], chain_id) + signatures |= set(signs) + packed_tx['signatures'] = list(signatures) + return json.dumps(packed_tx) + finally: + tx.free() def push_action(self, contract, action, args, permissions=None, compress=False, expiration=0, ref_block_id=None, indexes=None, payer=None, payer_permission="active"): if not permissions: diff --git a/pysrc/chainnative.py b/pysrc/chainnative.py index c959a63c..50be8a55 100644 --- a/pysrc/chainnative.py +++ b/pysrc/chainnative.py @@ -136,29 +136,27 @@ def gen_transaction(self, actions, expiration, reference_block_id, chain_id): expiration = int(time.time()) + 60 else: expiration = int(time.time()) + expiration - - tx = Transaction(expiration, reference_block_id, chain_id) - for a in actions: - contract, action_name, args, permissions = a - if isinstance(args, bytes): - args = args.hex() - elif isinstance(args, dict): - args = json.dumps(args) - elif isinstance(args, str): - pass - else: - tx.free() - raise Exception(f'Invalid args type: {type(args)}') - if isinstance(permissions, dict): - _permissions = permissions - permissions = [] - for actor in _permissions: - permissions.append({actor: _permissions[actor]}) - permissions = json.dumps(permissions) - self.check_abi(contract) - tx.add_action(contract, action_name, args, permissions) - try: + tx = Transaction(expiration, reference_block_id, chain_id) + for a in actions: + contract, action_name, args, permissions = a + if isinstance(args, bytes): + args = args.hex() + elif isinstance(args, dict): + args = json.dumps(args) + elif isinstance(args, str): + pass + else: + tx.free() + raise Exception(f'Invalid args type: {type(args)}') + if isinstance(permissions, dict): + _permissions = permissions + permissions = [] + for actor in _permissions: + permissions.append({actor: _permissions[actor]}) + permissions = json.dumps(permissions) + self.check_abi(contract) + tx.add_action(contract, action_name, args, permissions) return tx.marshal() finally: tx.free() diff --git a/pysrc/eosBase.py b/pysrc/eosBase.py index b9d94857..0c3345c5 100644 --- a/pysrc/eosBase.py +++ b/pysrc/eosBase.py @@ -288,81 +288,84 @@ def parse_unknown(data): @staticmethod def parse(json): - tx = Transaction() - tx.json = json - - tx.chain_id = binascii.unhexlify(json['chain_id']) - - body = json['transaction'] - -# expiration = int(datetime.strptime(body['expiration'], '%Y-%m-%dT%H:%M:%S').strftime("%s")) - delta = datetime.strptime(body['expiration'], '%Y-%m-%dT%H:%M:%S') - datetime(1970, 1, 1) - expiration = int(delta.total_seconds()) - tx.expiration = struct.pack('I', expiration) - tx.ref_block_num = struct.pack('H', body['ref_block_num']) - tx.ref_block_prefix = struct.pack('I', body['ref_block_prefix']) - tx.net_usage_words = struct.pack('B', body['max_net_usage_words']) - tx.max_cpu_usage_ms = struct.pack('B', body['max_cpu_usage_ms']) - tx.delay_sec = struct.pack('B', body['delay_sec']) - - tx.ctx_free_actions_size = struct.pack('B', len(body['context_free_actions'])) - tx.actions_size = struct.pack('B', len(body['actions'])) - - tx.actions = [] - for action in body['actions']: - act = Action() - act.account = Transaction.name_to_number(action['account']) - act.name = Transaction.name_to_number(action['name']) - - act.auth_size = struct.pack('B', len(action['authorization'])) - act.auth = [] - for auth in action['authorization']: - act.auth.append((Transaction.name_to_number(auth['actor']), Transaction.name_to_number(auth['permission']))) - - data = action['data'] - if isinstance(data, str): - parameters = bytes.fromhex(data) - elif action['name'] == 'transfer': - parameters = Transaction.parse_transfer(data) - elif action['name'] == 'voteproducer': - parameters = Transaction.parse_vote_producer(data) - elif action['name'] == 'buyram': - parameters = Transaction.parse_buy_ram(data) - elif action['name'] == 'buyrambytes': - parameters = Transaction.parse_buy_rambytes(data) - elif action['name'] == 'sellram': - parameters = Transaction.parse_sell_ram(data) - elif action['name'] == 'updateauth': - parameters = Transaction.parse_update_auth(data) - elif action['name'] == 'deleteauth': - parameters = Transaction.parse_delete_auth(data) - elif action['name'] == 'refund': - parameters = Transaction.parse_refund(data) - elif action['name'] == 'linkauth': - parameters = Transaction.parse_link_auth(data) - elif action['name'] == 'unlinkauth': - parameters = Transaction.parse_unlink_auth(data) - elif action['name'] == 'newaccount': - parameters = Transaction.parse_newaccount(data) - elif action['name'] == 'delegatebw': - parameters = Transaction.parse_delegate(data) - else: - parameters = Transaction.parse_unknown(data) - - act.data_size = Transaction.pack_fc_uint(len(parameters)) - act.data = parameters - - tx.actions.append(act) - - tx.tx_ext = struct.pack('B', len(body['transaction_extensions'])) - tx.cfd = binascii.unhexlify('00' * 32) - - for action in tx.actions: - sha = hashlib.sha256() - sha.update(action.data_size) - sha.update(action.data) - - return tx + try: + tx = Transaction() + tx.json = json + + tx.chain_id = binascii.unhexlify(json['chain_id']) + + body = json['transaction'] + + # expiration = int(datetime.strptime(body['expiration'], '%Y-%m-%dT%H:%M:%S').strftime("%s")) + delta = datetime.strptime(body['expiration'], '%Y-%m-%dT%H:%M:%S') - datetime(1970, 1, 1) + expiration = int(delta.total_seconds()) + tx.expiration = struct.pack('I', expiration) + tx.ref_block_num = struct.pack('H', body['ref_block_num']) + tx.ref_block_prefix = struct.pack('I', body['ref_block_prefix']) + tx.net_usage_words = struct.pack('B', body['max_net_usage_words']) + tx.max_cpu_usage_ms = struct.pack('B', body['max_cpu_usage_ms']) + tx.delay_sec = struct.pack('B', body['delay_sec']) + + tx.ctx_free_actions_size = struct.pack('B', len(body['context_free_actions'])) + tx.actions_size = struct.pack('B', len(body['actions'])) + + tx.actions = [] + for action in body['actions']: + act = Action() + act.account = Transaction.name_to_number(action['account']) + act.name = Transaction.name_to_number(action['name']) + + act.auth_size = struct.pack('B', len(action['authorization'])) + act.auth = [] + for auth in action['authorization']: + act.auth.append((Transaction.name_to_number(auth['actor']), Transaction.name_to_number(auth['permission']))) + + data = action['data'] + if isinstance(data, str): + parameters = bytes.fromhex(data) + elif action['name'] == 'transfer': + parameters = Transaction.parse_transfer(data) + elif action['name'] == 'voteproducer': + parameters = Transaction.parse_vote_producer(data) + elif action['name'] == 'buyram': + parameters = Transaction.parse_buy_ram(data) + elif action['name'] == 'buyrambytes': + parameters = Transaction.parse_buy_rambytes(data) + elif action['name'] == 'sellram': + parameters = Transaction.parse_sell_ram(data) + elif action['name'] == 'updateauth': + parameters = Transaction.parse_update_auth(data) + elif action['name'] == 'deleteauth': + parameters = Transaction.parse_delete_auth(data) + elif action['name'] == 'refund': + parameters = Transaction.parse_refund(data) + elif action['name'] == 'linkauth': + parameters = Transaction.parse_link_auth(data) + elif action['name'] == 'unlinkauth': + parameters = Transaction.parse_unlink_auth(data) + elif action['name'] == 'newaccount': + parameters = Transaction.parse_newaccount(data) + elif action['name'] == 'delegatebw': + parameters = Transaction.parse_delegate(data) + else: + parameters = Transaction.parse_unknown(data) + + act.data_size = Transaction.pack_fc_uint(len(parameters)) + act.data = parameters + + tx.actions.append(act) + + tx.tx_ext = struct.pack('B', len(body['transaction_extensions'])) + tx.cfd = binascii.unhexlify('00' * 32) + + for action in tx.actions: + sha = hashlib.sha256() + sha.update(action.data_size) + sha.update(action.data) + + return tx + finally: + tx.free() def encode(self): encoder = Encoder() diff --git a/pysrc/transaction.py b/pysrc/transaction.py index 1da13c1c..76901078 100644 --- a/pysrc/transaction.py +++ b/pysrc/transaction.py @@ -8,6 +8,14 @@ def __init__(self, expiration=0, ref_block=None, chain_id=None): self.idx = -1 return self.idx = _pyeoskit.transaction_new(expiration, ref_block, chain_id) + if self.idx == -1: + raise Exception("too many transactions has been created!") + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.free() @staticmethod def from_json(tx, chain_id=None): diff --git a/pytest/test_eosapi.py b/pytest/test_eosapi.py index dd7a1fae..859eba08 100644 --- a/pytest/test_eosapi.py +++ b/pytest/test_eosapi.py @@ -45,6 +45,13 @@ def setup_method(self, method): def teardown_method(self, method): pass + def test_tx_idx_overflow(self): + for i in range(1024+1): + account = 'eosio' + if i % 100 == 0: + logger.info('+++%s', i) + r = eosapi.push_action(account, 'sayhello', int.to_bytes(i, 8, 'little'), {account:'active'}) + def test_gen_transaction(self): args = { 'from': 'alice', @@ -354,6 +361,7 @@ def test_pack_tx(self): from pyeoskit import transaction t = transaction.Transaction() t.from_json(tx) + t.free() def test_crypto(self): key_pair = eosapi.create_key() diff --git a/src/pyeoskit b/src/pyeoskit index 506a7d3d..3305f80d 160000 --- a/src/pyeoskit +++ b/src/pyeoskit @@ -1 +1 @@ -Subproject commit 506a7d3d5fd80e385e9e022308fb2a8f2e86675c +Subproject commit 3305f80d0c93024fcbae60d211e2c0e8b28844b0