def test_wireup(self): """ Verify that the command is wired up correctly. The API method indeed calls the appropiate command. """ with patch( 'iota.commands.extended.get_transaction_objects.GetTransactionObjectsCommand.__call__', MagicMock(return_value='You found me!')) as mocked_command: api = Iota(self.adapter) # Don't need to call with proper args here. response = api.get_transaction_objects('hashes') self.assertTrue(mocked_command.called) self.assertEqual(response, 'You found me!')
class Collector(Crypt): def __init__(self, secret, node='https://nodes.thetangle.org:443'): super(Collector, self).__init__(secret) self.api = Iota(adapter=node) def getData(self, txHash): hashes = api.find_transactions(addresses=[ address, ])['hashes'] def decode(self, frag): hiddenMessage = frag.signature_message_fragment.decode() decrypted = self.decrypt(bytes(hiddenMessage, 'utf=8')) return json.loads(decrypted) def read(self, silent=False): address = Address(self.address) txHashes = self.api.find_transactions(addresses=[ address, ])['hashes'] batchSize = 80 sets = math.ceil(len(txHashes) / batchSize) txs = [] for i in range(sets): start = time.time() resp = self.api.get_transaction_objects( txHashes[i * batchSize:(i + 1) * batchSize])['transactions'] txs += resp duration = time.time() - start if not silent: print(f'Fetched {len(resp)} events in {duration:.1f} sec.') return sorted([self.decode(t) for t in txs], key=lambda a: a[0], reverse=True) def readLatest(self, silent=False): messages = self.read() secconds, msg = messages[0] timestamp = datetime.datetime.fromtimestamp(secconds) return f'{timestamp}: {msg}'
class Collector: def __init__(self, endpoint=None): self.api = Iota(adapter='https://nodes.thetangle.org:443') if endpoint is None: self.endpoint = Controller.endpoint else: self.endpoint = endpoint def getData(self, txHash): hashes = api.find_transactions(addresses=[ address, ])['hashes'] def read(self): address = Address(self.endpoint) txHashes = self.api.find_transactions(addresses=[ address, ])['hashes'] txs = self.api.get_transaction_objects(txHashes)['transactions'] decode = lambda a: json.loads(a.signature_message_fragment.decode()) return sorted([decode(t) for t in txs], key=lambda a: a[0], reverse=True)
class Escrow: def __init__(self, node='https://nodes.thetangle.org:443', seed=None): #Get Seed if seed is None: self.seed = self.getSeed() else: self.seed = seed #Setup API self.api = Iota(node, self.seed) #Generates a seed for escrow account def getSeed(self): #If no seed, create one if not os.path.isfile('seed.txt'): path = pathlib.Path(__file__).parent.absolute() seed = ''.join([random.choice(LETTERS) for i in range(81)]) open('seed.txt', 'w+').write(seed) logging.info("Placed new seed in seed.txt") return open('seed.txt').read().strip().encode('utf-8') #Creates an escrow holding address def createEscrow(self): try: self.holdingAddress = self.api.get_new_addresses( count=None, checksum=True)['addresses'][0] except iota.adapter.BadApiResponse as e: logging.warning("Bad response from server retrying.") return self.createEscrow() return self.holdingAddress #Waits for a transactions with a refund address def getRefundAddress(self): #This is the escrow address address = self.holdingAddress try: #Get Hashes from ledger txHashes = self.api.find_transactions(addresses=[ address, ])['hashes'] #If no hashes, user has not submitted an address yet. if len(txHashes) == 0: return None else: #Check messages for a valid address txs = self.api.get_transaction_objects( txHashes)['transactions'] for tx in txs: msg = tx.signature_message_fragment.decode() try: self.deposit = Address(msg.strip()) return self.deposit except: pass logging.warning("Invalid address recieved") except requests.exceptions.ConnectionError as e: #Sometimes the public nodes will reject a request print("Error contacting server; retrying") return self.getRefundAddress() #Cli version of escrow def startCli(self, collateral, fee=0, delay=120, deposit=None): #Create holding address self.createEscrow() self.fee = fee self.collateral = collateral #Wait for a deposit address to be entered if self.requestDeposit(collateral, deposit, delay): while not self.checkCondition(): sleep(3) self.finalizeEscrow() #Wait for escrow address to recieve collateral def requestDeposit(self, collateral, deposit=None, duration=120): #For CLI prompt a deposit address if deposit is None: self.deposit = input("What is the deposit address: ") print( f"You have {duration/60:.1f} min to deposit {collateral} IOTA to {self.holdingAddress}" ) #Wait for escrow to recive collateral funds. count = 0 while count < duration: time.sleep(1) balance = self.getBalance(self.holdingAddress) if balance >= collateral: print("Successfully deposited into escrow", balance) return True return False #Condition to release escrow def checkCondition(self): #Setup a check condition #For example RFID or some ledger condition return True #Refund user their collateral, remoing the fee def finalizeEscrow(self, fee=None, deposit=None): if fee is None: fee = self.fee if deposit is None: deposit = self.deposit #Return money to deposit address returnAmount = self.getBalance(self.holdingAddress) #Calcualte return amount if returnAmount > 0: returnAmount -= fee #Setup transaction message = "Repayment of collateral" feeLocation = self.api.get_new_addresses(count=1, checksum=True)['addresses'][0] txs = [ ProposedTransaction(address=Address(deposit), value=returnAmount, message=TryteString.from_unicode(message)), ] #Send transaction try: bundle = self.api.send_transfer(transfers=txs)['bundle'] except iota.adapter.BadApiResponse as e: print("Node did not respond. Retrying.") return self.finalizeEscrow(fee, deposit) logging.info(bundle.transactions[0].hash) logging.info("Sent money back to recipient") self.addRevenue(fee) def getBalance(self, address): try: response = self.api.get_balances(addresses=[address])['balances'] return response[0] except requests.exceptions.ConnectionError as e: logging.info("Error contacting server; retrying") return self.getBalance(self, address) #Record the amount of revenue recieved def addRevenue(self, money, filename='revenue.txt'): if not os.path.isfile(filename): open(filename, 'w+').write('0') current = int(open(filename).read().strip()) current += money open(filename, 'w+').write(str(current)) #Get the current amount of revenue def getRevenue(self, filename="revenue.txt"): if not os.path.isfile(filename): return 0 return int(open(filename).read().strip()) #Send revenue to an address def sendRevenue(self, outputAddress): revenue = self.getRevenue() logger.info(f"Currently have {revenue} revenue.") message = "Output fees from escrow." txs = [ ProposedTransaction(address=Address(outputAddress), value=revenue, message=TryteString.from_unicode(message)), ] try: logger.info("Sending transfer to node.") bundle = self.api.send_transfer(transfers=txs)['bundle'] except iota.adapter.BadApiResponse as e: print("Bad api resonse retrying") return self.sendRevenue(outputAddress) print(bundle.transactions[0].hash)