def make_neighbors(step, node1, node2): host1 = world.machine['nodes'][node1]['podip'] port1 = world.machine['nodes'][node1]['clusterip_ports']['gossip-udp'] host2 = world.machine['nodes'][node2]['podip'] port2 = world.machine['nodes'][node2]['clusterip_ports']['gossip-udp'] hosts = [host1, host2] ports = [port1, port2] api1 = api_utils.prepare_api_call(node1) api2 = api_utils.prepare_api_call(node2) response1 = api1.get_neighbors() response2 = api2.get_neighbors() neighbors1 = list(response1['neighbors']) neighbors2 = list(response2['neighbors']) host = hosts[0] port = ports[0] address1 = "udp://" + str(host) + ":" + str(port) host = hosts[1] port = ports[1] address2 = "udp://" + str(host) + ":" + str(port) logger.debug("Checking if nodes are paired") containsNeighbor = False for neighbor in range(len(neighbors1)): if neighbors1[neighbor]['address']: containsNeighbor = True logger.debug("Neighbor found") if containsNeighbor == False: api1.add_neighbors([address2.decode()]) api2.add_neighbors([address1.decode()]) logger.debug("Nodes paired") containsNeighbor = False for neighbor in range(len(neighbors2)): if neighbors2[neighbor]['address']: containsNeighbor = True logger.debug("Neighbor found") if containsNeighbor == False: api2.add_neighbors([address1.decode()]) api1.add_neighbors([address2.decode()]) logger.debug("Nodes paired") response = api1.get_neighbors() logger.info(response) response = api2.get_neighbors() logger.info(response)
def find_transactions_from_address(step): logger.info('Finding milestones') node = world.config['nodeId'] api = api_utils.prepare_api_call(node) transactions = api.find_transactions(addresses=[step.multiline]) world.responses['findTransactions'] = transactions
def issue_stitching_transaction(step, node, tag): world.config['nodeId'] = node world.config['stitchTag'] = tag api = api_utils.prepare_api_call(node) logger.info('Finding Transactions') side_tangle_transaction = static.SIDE_TANGLE_TRANSACTIONS[0] gtta_transactions = api.get_transactions_to_approve(depth=3) trunk = side_tangle_transaction branch = gtta_transactions['branchTransaction'] stitching_address = static.STITCHING_ADDRESS logger.debug('Trunk: ' + str(trunk)) logger.debug('Branch: ' + str(branch)) bundle = transactions.create_transaction_bundle(stitching_address, tag, 0) trytes = bundle[0].as_tryte_string() sent_transaction = api.attach_to_tangle(trunk, branch, [trytes], 14) api.broadcast_and_store(sent_transaction.get('trytes')) # Finds transaction hash and stores it in world bundlehash = api.find_transactions(bundles=[bundle.hash]) if 'previousTransaction' not in world.responses: world.responses['previousTransaction'] = {} world.responses['previousTransaction'][node] = bundlehash['hashes'][0]
def find_transaction_is_called(step, nodeName): logger.debug(nodeName) api = api_utils.prepare_api_call(nodeName) logger.info("Searching for Transaction with the tag: {} on {}".format( world.config['tag'], nodeName)) response = api.find_transactions(tags=[world.config['tag']]) world.config['findTransactionResponse'] = response
def threaded_call(step, apiCall, node): logger.info("Creating thread for {}".format(apiCall)) world.config['apiCall'] = apiCall world.config['nodeId'] = node arg_list = step.hashes options = {} api_utils.prepare_options(arg_list, options) api = api_utils.prepare_api_call(node) def make_call(node, arg_list): response = api_utils.fetch_call(apiCall, arg_list['api'], arg_list['options']) arg_list['responses'][apiCall] = {} arg_list['responses'][apiCall][node] = response return response args = { node: { 'api': api, 'options': options, 'responses': world.responses } } future_results = pool.start_pool(make_call, 1, args) if 'future_results' not in world.config: world.config['future_results'] = {} world.config['future_results'][apiCall] = future_results
def make_neighbors(step, node1, node2): """ Ensures that the specified nodes are neighbored with one another. :param node1: The identifier for the first node (ie nodeA) :param node2: The identifier for the second node (ie nodeB) """ neighbor_candidates = [node1, node2] neighbor_info = {} for node in neighbor_candidates: host = world.machine['nodes'][node]['podip'] port = world.machine['nodes'][node]['clusterip_ports']['gossip-udp'] api = api_utils.prepare_api_call(node) response = api.get_neighbors() neighbor_info[node] = { 'api': api, 'node_neighbors': list(response['neighbors']), 'address': str(host) + ":" + str(port) } logger.info('Checking neighbors for {}'.format(node1)) neighbors.check_if_neighbors(neighbor_info[node1]['api'], neighbor_info[node1]['node_neighbors'], neighbor_info[node2]['address']) logger.info('Checking neighbors for {}'.format(node2)) neighbors.check_if_neighbors(neighbor_info[node2]['api'], neighbor_info[node2]['node_neighbors'], neighbor_info[node1]['address'])
def issue_multiple_transactions(step, num_transactions, node): transactions_to_store = [] world.responses['evaluate_and_send'] = {} world.config['nodeId'] = node # Placeholder values for seed if present seed_value = "" seed_type = "" for arg_index, arg in enumerate(step.hashes): if arg['keys'] == "seed" and arg['type'] == "staticList": seed_value = arg['values'] seed_type = arg['type'] for iteration in range(int(num_transactions)): seed = "" if seed_value != "" and seed_type == "staticList": seed = getattr(static, seed_value)[iteration] api = api_utils.prepare_api_call(node, seed=seed) logger.info('Sending Transaction {}'.format(iteration + 1)) transaction = transactions.evaluate_and_send(api, seed, step.hashes) transaction_hash = Transaction.from_tryte_string( transaction.get('trytes')[0]).hash transactions_to_store.append(transaction_hash) world.responses['evaluate_and_send'][node] = transactions_to_store logger.info("Transactions generated and stored")
def generate_transaction_and_attach(step, node): """ Creates a zero value transaction with the specified arguments. :param node: The node that the transaction will be generated on. :param step.hashes: A gherkin table present in the feature file specifying the arguments and the associated type. """ arg_list = step.hashes world.config['nodeId'] = node world.config['apiCall'] = 'attachToTangle' options = {} api = api_utils.prepare_api_call(node) api_utils.prepare_options(arg_list, options) transaction_args = {} for key in options: transaction_args[key] = options.get(key) api_utils.prepare_transaction_arguments(transaction_args) transaction = transactions.create_and_attach_transaction( api, transaction_args) api.broadcast_and_store(transaction.get('trytes')) assert len( transaction['trytes']) > 0, "Transaction was not created correctly" world.responses['attachToTangle'] = {} world.responses['attachToTangle'][node] = transaction logger.info('Transaction Sent') setattr(static, "TEST_STORE_TRANSACTION", transaction.get('trytes'))
def issue_a_milestone_with_reference(step, index): """ This method issues a milestone with a given index and reference transaction. The input transaction pointer should always have the key "transactions", but may be a pointer to either a staticValue list stored in staticValues.py, or a responseList for "findTransactions". :param index: The index of the milestone you are issuing """ node = world.config['nodeId'] address = static.TEST_BLOWBALL_COO api = api_utils.prepare_api_call(node) reference_transaction = transactions.fetch_transaction_from_list( step.hashes, node) logger.info('Issuing milestone {}'.format(index)) milestone = milestones.issue_milestone(address, api, index, reference_transaction) if 'latestMilestone' not in world.config: world.config['latestMilestone'] = {} milestone_hash = Transaction.from_tryte_string(milestone['trytes'][0]).hash milestone_hash2 = Transaction.from_tryte_string( milestone['trytes'][1]).hash world.config['latestMilestone'][node] = [milestone_hash, milestone_hash2]
def generate_transaction_and_attach(step, node): arg_list = step.hashes world.config['nodeId'] = node world.config['apiCall'] = 'attachToTangle' options = {} api = api_utils.prepare_api_call(node) api_utils.prepare_options(arg_list, options) addresses = options.get('address') value = options.get('value') transaction = ProposedTransaction(address=Address(addresses[0]), value=value) bundle = ProposedBundle() bundle.add_transaction(transaction) bundle.finalize() trytes = str(bundle[0].as_tryte_string()) gtta = api.get_transactions_to_approve(depth=3) branch = str(gtta['branchTransaction']) trunk = str(gtta['trunkTransaction']) sent = api.attach_to_tangle(trunk, branch, [trytes], 9) world.responses['attachToTangle'] = {} world.responses['attachToTangle'][node] = sent logger.info('Transaction Sent') setattr(static_vals, "TEST_STORE_TRANSACTION", sent.get('trytes'))
def create_inconsistent_transaction(step, node): """ Creates an inconsistent transaction by generating a zero value transaction that references a non-existent transaction as its branch and trunk, thus not connecting with any other part of the tangle. :param node: The node that the transaction will be generated on. """ world.config['nodeId'] = node api = api_utils.prepare_api_call(node) trunk = static.NULL_HASH branch = trunk trytes = static.EMPTY_TRANSACTION_TRYTES argument_list = { 'trunk_transaction': trunk, 'branch_transaction': branch, 'trytes': [trytes], 'min_weight_magnitude': 14 } transaction = transactions.attach_store_and_broadcast(api, argument_list) transaction_trytes = transaction.get('trytes') transaction_hash = Transaction.from_tryte_string(transaction_trytes[0]) if 'inconsistentTransactions' not in world.responses: world.responses['inconsistentTransactions'] = {} world.responses['inconsistentTransactions'][node] = transaction_hash.hash
def reference_stitch_transaction(step): node = world.config['nodeId'] stitch = world.responses['previousTransaction'][node] referencing_address = static.REFERENCING_ADDRESS api = api_utils.prepare_api_call(node) transaction_bundle = transactions.create_transaction_bundle(referencing_address, 'REFERENCE9TAG', 0) def transactions_to_approve(node, arg_list): response = api_utils.fetch_call('getTransactionsToApprove', arg_list['api'], {'depth': 3}) arg_list['responses']['getTransactionsToApprove'][node] = response return response gtta_results = pool.start_pool(transactions_to_approve, 1, {node: {'api': api, 'responses': world.responses}}) branch = pool.fetch_results(gtta_results[0], 30) options = {'trunk': stitch, 'branch': branch, 'trytes': transaction_bundle.as_tryte_strings(), 'min_weight_magnitude': 9} def make_transaction(node, arg_list): response = transactions.attach_store_and_broadcast(arg_list['api'], options) arg_list['responses']['attachToTangle'][node] = response return response transaction_results = pool.start_pool(make_transaction, 1, {node: {'api': api, 'responses': world.responses}}) pool.fetch_results(transaction_results[0], 30)
def check_stitch_consistency(step): logger.info('Checking consistency of stitching transaction') node = world.config['nodeId'] api = tests.prepare_api_call(node) transaction = world.responses['previousTransaction'] timeout = False def consistency(responses): consistency_response = api.check_consistency(tails=[transaction]) responses['checkConsistency'] = consistency_response return try: r = threading.Thread(target=consistency, args=(world.responses, )) r.setDaemon(True) r.start() r.join(timeout=60) if r.is_alive(): raise ValueError(TIMEOUT_ERROR) except ValueError as error: logger.info('Failed') logger.info(error) timeout = True if timeout != True: logger.debug('Response: {}'.format( world.responses['checkConsistency'])) logger.info('Consistency check completed, request did not time out') else: raise ValueError(TIMEOUT_ERROR)
def call_getTrytes(step, node, hash): api = api_utils.prepare_api_call(node) testHash = getattr(static_vals, hash) response = api.get_trytes([testHash]) logger.debug("Call may not have responded correctly: \n%s", response) assert type(response) is dict world.responses['getTrytes'] = {} world.responses['getTrytes'][node] = response
def issue_several_milestones(step, num_milestones): node = world.config['nodeId'] api = api_utils.prepare_api_call(node) latest_milestone_index = int( api.get_node_info()['latestSolidSubtangleMilestoneIndex']) logger.info('Latest Milestone Index: {}'.format(latest_milestone_index)) start_index = latest_milestone_index + 1 end_index = start_index + int(num_milestones) for index in range(start_index, end_index): issue_a_milestone(step, index, node) #Give node a moment to update solid milestone wait_for_update(index, api)
def create_inconsistent_transaction(step, node): world.config['nodeId'] = node api = api_utils.prepare_api_call(node) branch = getattr(static_vals, "NULL_HASH") trunk = branch trytes = getattr(static_vals, "EMPTY_TRANSACTION_TRYTES") transaction = api.attach_to_tangle(trunk, branch, [trytes], 14) transaction_trytes = transaction.get('trytes') api.store_transactions(transaction_trytes) transaction_hash = Transaction.from_tryte_string(transaction_trytes[0]) logger.info(transaction_hash.hash) if 'inconsistentTransactions' not in world.responses: world.responses['inconsistentTransactions'] = {} world.responses['inconsistentTransactions'][node] = transaction_hash.hash
def check_neighbors(step): api = api_utils.prepare_api_call(world.config['nodeId']) response = api.getNeighbors() containsNeighbor = [False, False] for i in response: expectedNeighbors = step.hashes if type(response[i]) != int: for x in range(len(response[i])): if expectedNeighbors[0]['neighbors'] == response[i][x][ 'address']: containsNeighbor[0] = True if expectedNeighbors[1]['neighbors'] == response[i][x][ 'address']: containsNeighbor[1] = True return containsNeighbor
def reference_stitch_transaction(step): node = world.config['nodeId'] stitch = world.responses['previousTransaction'][node] referencing_address = static.REFERENCING_ADDRESS api = api_utils.prepare_api_call(node) transaction_bundle = transactions.create_transaction_bundle( referencing_address, 'REFERENCE9TAG', 0) branch = api.get_transactions_to_approve(depth=3)['branchTransaction'] options = { 'trunk_transaction': stitch, 'branch_transaction': branch, 'trytes': transaction_bundle.as_tryte_strings(), 'min_weight_magnitude': 9 } transactions.attach_store_and_broadcast(api, options)
def send_transaction(step, tag, nodeName): logger.debug('Preparing Transaction...') logger.debug('Node: %s', nodeName) world.config['tag'] = tag api = api_utils.prepare_api_call(nodeName) txn = \ ProposedTransaction( address = Address(testAddress), message = TryteString.from_unicode('Test Transaction propagation'), tag = Tag(tag), value = 0, ) logger.info("Sending Transaction with tag '{}' to {}...".format( tag, nodeName)) txn_sent = api.send_transfer(depth=3, transfers=[txn]) logger.debug("Giving the transaction time to propagate...") sleep(10)
def spam_call_gtta(step, numTests): """Spams getTransactionsToApprove calls a number of times among available nodes in a cluster""" start = time() node = next(iter(world.machine['nodes'])) apiCall = 'getTransactionsToApprove' world.config['nodeId'] = node world.config['apiCall'] = apiCall nodes = {} for current_node in world.machine['nodes']: api = api_utils.prepare_api_call(current_node) nodes[current_node] = api def run_call(node, api): logger.debug('Running Thread on {}'.format(node)) response = api.get_transactions_to_approve(depth=3) return response logging.info('Calls being made to %s', node) responseVal = [] args = (nodes) future_results = pool.start_pool(run_call, numTests, args) i = 0 for result in future_results: i += 1 if i % 25 == 0: logger.info('Fetching result: {}/{}'.format(i, numTests)) response = pool.fetch_results(result, 30) responseVal.append(response) logger.info(len(responseVal)) world.responses[apiCall] = {} world.responses[apiCall][node] = responseVal end = time() time_spent = end - start logger.info('Time spent on loop: {}'.format(time_spent))
def reference_stitch_transaction(step): node = world.config['nodeId'] stitch = world.responses['previousTransaction'] api = tests.prepare_api_call(node) timeout = False def transactions_to_approve(responses): response = api.get_transactions_to_approve(depth=3) responses['getTransactionsToApprove'] = response return try: t = threading.Thread(target=transactions_to_approve, args=(world.responses, )) t.setDaemon(True) t.start() t.join(timeout=60) if t.is_alive(): raise ValueError(TIMEOUT_ERROR) except ValueError as error: logger.info('Failed') logger.info(error) timeout = True if timeout != True: branch = world.responses['getTransactionsToApprove'][ 'branchTransaction'] bundle = transactions.create_transaction_bundle( referencing_address, 'REFERENCING9STITCH', 0) trytes = bundle[0].as_tryte_string() sent_transaction = api.attach_to_tangle(stitch, branch, [trytes], 14) api.broadcast_and_store(sent_transaction.get('trytes')) else: raise ValueError(TIMEOUT_ERROR)
def issue_a_milestone_with_reference(step, index): """ This method issues a milestone with a given index and reference transaction. The input transaction pointer should always have the key "transactions", but may be a pointer to either a staticValue list stored in staticValues.py, or a responseList for "findTransactions". :param index: The index of the milestone you are issuing :param step.hashes: Contains a reference pointer for list of transactions to get a reference from """ node = world.config['nodeId'] address = static.TEST_BLOWBALL_COO api = api_utils.prepare_api_call(node) reference_transaction = transactions.fetch_transaction_from_list( step.hashes, node) logger.info('Issuing milestone {}'.format(index)) milestone = milestones.issue_milestone(address, api, index, reference_transaction) milestones.update_latest_milestone(world.config, node, milestone)
def issue_a_milestone(step, index, node): """ This method issues a milestone with a given index. :param index: The index of the milestone you are issuing :param node: The node that the milestone will be attached to """ world.config['nodeId'] = node address = static.TEST_BLOWBALL_COO api = api_utils.prepare_api_call(node) logger.info('Issuing milestone {}'.format(index)) milestone = milestones.issue_milestone(address, api, index) if 'latestMilestone' not in world.config: world.config['latestMilestone'] = {} milestone_hash = Transaction.from_tryte_string(milestone['trytes'][0]).hash milestone_hash2 = Transaction.from_tryte_string( milestone['trytes'][1]).hash world.config['latestMilestone'][node] = [milestone_hash, milestone_hash2]
def generate_transaction_and_attach(step, node): """ Creates a transaction with the specified arguments. :param node: The node that the transaction will be generated on. :param step.hashes: A gherkin table present in the feature file specifying the arguments and the associated type. """ world.config['nodeId'] = node world.config['apiCall'] = 'attachToTangle' seed = transactions.check_for_seed(step.hashes) api = api_utils.prepare_api_call(node, seed=seed) transaction = transactions.evaluate_and_send(api, seed, step.hashes) assert len(transaction['trytes']) > 0, "Transaction was not created correctly" world.responses['attachToTangle'] = {} world.responses['attachToTangle'][node] = transaction setattr(static, "TEST_STORE_TRANSACTION", transaction.get('trytes')) return transaction
def api_method_is_called(step, apiCall, nodeName): """ This is the general api calling function. There are 3 inputs :param apiCAll: The api call that will be requested :param nodeName: The name identifying the node you would like to make this request on :param table: A gherkin table outlining any arguments needed for the call (See tests/features/machine1/1_api+tests.feature for examples) The table parameter is unique in that there are several input types available depending on the call being made. :type string: Basic string argument, will be taken as is :type int: Basic integer argument, will be converted to int before call is made :type nodeAddress: Node name identifier, will create address from node configuration :type staticValue: Static name identifier, will fetch value from util/static_vals.py :type staticList: Same as staticValue, except it places the results into a list :type responseValue: Identifier for api call response value :type responseList: Same as responseValue, ecept it places the results into a list :type bool: Bool argument, returns True or False """ logger.info('%s is called on %s', apiCall, nodeName) world.config['apiCall'] = apiCall world.config['nodeId'] = nodeName arg_list = step.hashes options = {} api_utils.prepare_options(arg_list, options) api = api_utils.prepare_api_call(nodeName) response = api_utils.fetch_call(apiCall, api, options) assert type( response ) is dict, 'There may be something wrong with the response format: {}'.format( response) world.responses[apiCall] = {} world.responses[apiCall][nodeName] = response
def threaded_call(step, api_call, node): """ Makes an asynchronous API call on the specified node and stores the future result reference in the world.config variable. :param api_call: The API call you would like to make. :param node: The identifier for the node you would like to run the call on. :param step.hashes: A gherkin table present in the feature file specifying the arguments and the associated type. """ logger.info("Creating thread for {}".format(api_call)) world.config['apiCall'] = api_call world.config['nodeId'] = node arg_list = step.hashes options = {} api_utils.prepare_options(arg_list, options) api = api_utils.prepare_api_call(node) def make_call(node, arg_list): response = api_utils.fetch_call(api_call, arg_list['api'], arg_list['options']) arg_list['responses'][api_call] = {} arg_list['responses'][api_call][node] = response return response args = { node: { 'api': api, 'options': options, 'responses': world.responses } } future_results = pool.start_pool(make_call, 1, args) if 'future_results' not in world.config: world.config['future_results'] = {} world.config['future_results'][api_call] = future_results