''' ::: interface.py ::: ::: IPFS <> blockchain interface ::: ''' import os, json from web3 import Web3, HTTPProvider, IPCProvider from random import randint # ipfs daemon needs to be running first class BCInterface: # construct points to ddash contract address on blackswan Ethereum network # by default def __init__(self,host='localhost',port=5001,mainnet=False): self.last_contract_address = None self.last_hash_added = None #self.api = ipfsapi.connect(host='127.0.0.1',port=port) # self.web3 = Web3(HTTPProvider('http://localhost:8545')) if mainnet: ipc_path=os.path.dirname(os.path.realpath(__file__))+'/data_mainnet/geth.ipc' else: ipc_path = os.path.dirname(os.path.realpath(__file__))+'/data/geth.ipc' print("IPCProvider path: ",ipc_path) self.web3 = Web3(IPCProvider(ipc_path)) self.blockNumber = self.web3.eth.blockNumber self.eth_accounts = self.web3.personal.listAccounts self.account_index = 0 self.ethereum_acc_pass = None self.tx = {} print("Initializing a DDASH Interface object.") # log Ethereum accounts to ddash/nfo/ self.write_ethereum_address(mainnet) # contract_name is without the sol extension def load_contract(self,contract_name,sender_address=None,contract_address="0x40a4dcb3fdcbaa00848d2c14386abed56797bf61"): if not sender_address: sender_address = self.eth_accounts[0] self.tx['to'] = contract_address self.tx['from'] = sender_address abi = '' contract_name_lower = contract_name.lower()+'.abi' abi_path = os.path.dirname(os.path.realpath(__file__))+'/source/'+contract_name_lower print("Loading contract "+contract_name_lower) print("from directory: "+abi_path) print("Sender address: "+sender_address) print("Contract address: "+contract_address) print(abi_path) with open(abi_path,'r') as myfile: abi+=myfile.read() json_abi = json.loads(abi) self.contract = self.web3.eth.contract(abi=json_abi,address=contract_address) if self.contract: print("You are now interfacing with contract at address "+contract_address) def show_eth_accounts(self): if len(self.eth_accounts) ==0: print("You have no Ethereum accounts. Create a new account by typing 'new account'") return 0 print("I found the following Ethereum accounts:") for i, acc in enumerate(self.eth_accounts): print(i,"\t\t",acc) def get_eth_accounts(self): if len(self.eth_accounts) == 0: print("You have no Ethereum accounts. Create a new account by typing 'new account'") return 0 return self.eth_accounts def sanity_check(self): if not (self.api): print("I don't see IPFS running. Please make sure IPFS daemon is running first.") return 1 if not (self.blockNumber): print("I don't see geth running. Please run the go Ethereum client in the background.") return 1 if self.api and self.blockNumber: print("IPFS and geth appear to be running.") return 0 def random(self): assert(self.contract) assert(self.tx) i = randint(0,9) return self.contract.transact(self.tx).randomGen(i) def heyo(self): assert(self.contract) i = randint(0,4) print(self.contract.call().greet_omar(i)) return 0 def upload_to_ipfs(self,filepath): assert(os.path.isfile(filepath)) assert(self.api) self.last_hash_added = result = self.api.add(filepath) if self.last_hash_added: print("'"+result['Name']+"' was uploaded to IPFS with hash:\n "+result['Hash']) return result['Name'],result['Hash'] print("Failed to upload file "+str(filepath)+" to IPFS") return 1 def add_record(self,owner_address,filename,ipfs_hash,description): print("adding record to blockchain:") print("owner_adddress:",owner_address) print("filename:",filename) print("ipfs_hash:",ipfs_hash) print("description",description) return self.contract.transact(self.tx).addRecord(ipfs_hash,filename,description) def get_record_by_row(self,row): self.contract.transact(self.tx).getRecordByRow(row) return self.contract.call().getRecordByRow(row) def get_record_by_ipfs_hash(self,ipfs_hash): self.contract.transact(self.tx).get_record_by_ipfs_hash(ipfs_hash) return self.contract.call().get_record_by_ipfs_hash(ipfs_hash) def get_record_count(self): self.contract.transact(self.tx).getRecordCount() return self.contract.call().getRecordCount() # unlock selected Ethereum account def unlock_account(self, password): if len(self.eth_accounts) ==0: print("No Ethereum account found. Create a new account by typing 'new account'") else: print("Attempting to unlock account ",str(self.eth_accounts[self.account_index])) self.web3.personal.unlockAccount(self.eth_accounts[self.account_index],password) # select Ethereum account def set_account(self,index): if len(self.eth_accounts) ==0: print("No Ethereum account found. Create a new account by typing 'new account'") elif index >= len(self.eth_accounts): print("Invalid index.") else: print("You are now using account index ",index) self.account_index = index #self.load_contract(sender_address=self.eth_accounts[self.account_index]) # get number of enodes on the blockchain def peer_count(self): print(str(self.contract.call().get_entity_count())+" enodes found on the blockchain.") return 0 def get_balance(self): return self.web3.eth.getBalance(self.eth_accounts[self.account_index]) def get_address(self): return self.eth_accounts[0] def write_ethereum_address(self,mainnet=False): nfo_path = os.path.dirname(os.path.realpath(__file__))+'/nfo/eth_addresses.ds' file_text='' if os.path.isfile(nfo_path): with open(nfo_path,'r') as myfile: file_text+=myfile.read() if self.eth_accounts[0]: if self.eth_accounts[0] not in file_text: with open(nfo_path,'a') as fileout: if mainnet: fileout.write('mn:'+self.eth_accounts[0]+'\n') else: fileout.write('pn:'+self.eth_accounts[0]+'\n') def get_ethereum_address(self): nfo_path = os.path.dirname(os.path.realpath(__file__))+'/nfo/eth_addresses.ds' file_text='' mainnet_eth_address = None privatenet_eth_address = None if os.path.isfile(nfo_path): with open(nfo_path,'r') as myfile: file_text+=myfile.read() x = file_text.split('pn:') y = x[1].split('mn:') privatenet_eth_address = y[0] mainnet_eth_address = y[1] self.privatenet_eth_address = privatenet_eth_address self.mainnet_eth_address = mainnet_eth_address return privatenet_eth_address, mainnet_eth_address def increase_gas(self,exp): if 'gas' not in self.tx.keys(): self.tx['gas'] = 10000 new_gas_value = self.tx['gas'] * 10**exp print("Increasing gas to ", new_gas_value) self.tx['gas'] = new_gas_value return new_gas_value def decrease_gas(self,exp): if 'gas' not in self.tx.keys(): self.tx['gas'] = 10000 new_gas_value = self.tx['gas'] / 10**exp print("Decreasing gas to ", new_gas_value) self.tx['gas'] = new_gas_value return new_gas_value def is_valid_contract_address(self, addr): if len(addr) == 42: return addr else: return False