def getAllKeys(self, params): wal = 'unidentified' try: wal = params['wallet'] pattern = re.compile(regexWallet) if not pattern.match(wal): return errMsg("Invalid wallet name.") cmd = "SELECT" sel = False if "py" in params['sel']: cmd = cmd + " 'pubkey:' || pubKey," sel = True if "ay" in params['sel']: cmd = cmd + " 'addr:' || address," sel = True if "ny" in params['sel']: cmd = cmd + " 'name:' || KName" sel = True if sel is False: cmd = cmd + " pubKey, address, KName" else: if cmd.endswith(","): cmd = cmd[0:-1] cmd = cmd + " FROM Wallet WHERE WName='" + wal + "'" return setOK({"keyList": self.doSelect(cmd)}) except Exception: return errMsg("Collecting keys for wallet " + wal + " failed.")
def createKeys(self, data): wal = data['name'] if not self.hasWallet(wal): return errMsg("Wallet '" + wal + "' not found.") cmd = "SELECT DISTINCT KName from Wallet WHERE User='******'user'] + "' AND WName='" + wal + "' AND (" knames = "" pattern = re.compile(regexWallet) for idx in range(0, data['numKeys']): if len(data['keyNames']) > idx: if len(knames) > 0: knames = knames + " OR " if not pattern.match(data['keyNames'][idx]): return errMsg( "Invalid key name, use a-z, aA-Z and numbers only.") knames = knames + "KName='" + data['keyNames'][idx] + "'" if len(knames) > 0: cmd = cmd + knames + ")" rpl = self.doSelect(cmd) if len(rpl) > 0: cmd = "Duplicate name(s):" for nam in rpl: cmd = cmd + nam + "," return errMsg(cmd + " no key(s) generated") return self.addKeysToWallet(data, wal)
def getKeyBalance(self, params): keys = self.getDataFor([params['sel'], params['key']], params['wallet'], "", params['user']) if len(keys) > 4: respText, respCode = self.collectKeyBalance(keys[4]) if respCode == 200: return setOK(respText) else: return errMsg(respText['errorMsg'], respCode) return errMsg("Invalid parameters provided")
def getTXForHash(self, hash): if (len(hash) != len(defHash)): return errMsg("Invalid Hash Len") response = self.getPendTXByHash(hash) # if a given hash is found already, no other exists, else keep searching if (len(hash) == 0) or (len(response) == 0): response.extend(self.getConfirmedTXByHash(hash)) if (len(response) > 0): return setOK(response) return errMsg("No transactions found for: " + hash, 404)
def setID(self, data): if m_data['chainRef'] != "": return errMsg("Current chainRef set: " + m_data['chainRef']) if project.classes.c_walletInterface.hasWallet( 'genesis' + data['chainRef']) is True: return errMsg("Wallet reference already exists") m_data.clear() m_data.update(deepcopy(m_dataInit)) m_data['chainRef'] = data['chainRef'] return setOK("chain ID set to: " + data['chainRef'])
def getMinerCandidate(self, minerAddress): #TODO share same block for different miner later on to save memory if m_cfg['chainLoaded'] is False: sleep(1) return errMsg("No chain loaded yet, retry ....") err = verifyAddr(minerAddress) if len(err) > 0: return errMsg(err) if minerAddress in m_BufferMinerCandidates: cand = m_BufferMinerCandidates[minerAddress]['mineCandidate'] if cand['index'] == m_candidateBlock['index']: if m_BufferMinerCandidates[minerAddress]['minerBlock']['blockDataHash'] == m_candidateBlock['blockDataHash']: return setOK(cand) # if nothing has changed, return same block # as candidate blocks change with every new TX and different miners might deal # with different blocks, we must keep the miner specific block in case a miner succeeds #if 'prevBlockHash' not in m_candidateBlock: self.c_blockchainHandler.prepareNewCandidateBlock() minerSpecificBlock = deepcopy(m_candidateBlock) # TODO need house-keeping if miners disappear and don't come back, # else we get a queue overflow attack by fake miners candidateMiner = deepcopy(m_candidateMiner) candidateMiner['rewardAddress'] = minerAddress minerSpecificBlock['minedBy'] = minerAddress fees = minBlockReward bindex = len(m_Blocks) for tx in m_candidateBlock['transactions']: if len(tx) > 0: #otherwise it is empty coinbase fees = fees + tx['fee'] else: if fees != minBlockReward: return errMsg("Invalid minimum CoinBase fee TX in block", 404) coinBase = deepcopy(m_coinBase) candidateMiner['index'] = bindex coinBase['minedInBlockIndex'] = bindex minerSpecificBlock['index'] = candidateMiner['index'] candidateMiner['expectedReward'] = fees coinBase['value'] = fees coinBase['to'] = minerAddress coinBase['dateCreated'] = getTime() coinBase['transactionDataHash'] = sha256ToHex(m_transaction_order, coinBase) minerSpecificBlock['transactions'].insert(0, coinBase) candidateMiner['transactionsIncluded'] = len(minerSpecificBlock['transactions']) #inlcudes coinbase # now the block is done, hash it for miner # need to calculate now the hash for this specific miner based candidateBlock # the hash for the miner has to be in specific order of data candidateMiner['blockDataHash'] = makeBlockDataHash(m_candidateBlock, False) print("Generate new candidate for miner: " + minerAddress + " with Hash: " + candidateMiner['blockDataHash'] + " reward: " + str(fees)) m_BufferMinerCandidates[minerAddress] = {} m_BufferMinerCandidates[minerAddress]['mineCandidate'] = deepcopy(candidateMiner) m_BufferMinerCandidates[minerAddress]['minerBlock'] = minerSpecificBlock return setOK(candidateMiner)
def updGX(self, data): if m_data['chainRef'] == "": return errMsg("Missing chainRef ") if data['chainRef'] != m_data['chainRef']: return errMsg("Current chainRef not matching: " + m_data['chainRef']) for jtx in data['TXList']: if jtx['chainRef'] != m_data['chainRef']: return errMsg("One of the TX has invalid chainRef: " + jtx['chainRef']) m_data.clear() m_data.update(data) return setOK("Data updated without major verifications")
def getAllBalance(self, params): try: user = params['user'] bal = deepcopy(m_balanceData) for key in self.doSelect( "SELECT DISTINCT address FROM Wallet WHERE User='******'"): val, respCode = self.collectKeyBalance(key) if respCode == 200: self.sumBalance(bal, val) else: return errMsg(val, respCode) return setOK(bal) except Exception: return errMsg("Seems to have peer issues....")
def genesis_POST(): linkInfo = {} try: values = request.get_json() except Exception: return errMsg("JSON not decodeable") return c_MainIntf.nodeSpecificPOST(request.path, linkInfo, values, request)
def loadSys(self,sysIn): self.c_blockchainHandler.clearChainLoadGenesis() myUrl = m_info['nodeUrl'] myNodeID = m_info['nodeId'] m_info.clear() m_info.update(sysIn['m_info']) m_info["nodeUrl"] = myUrl m_info["nodeId"] = myNodeID m_cfg.clear() m_cfg.update(sysIn['m_cfg']) m_peerInfo.clear() m_peerInfo.update(sysIn['m_peerInfo']) m_Blocks.clear() for block in sysIn['m_Blocks']: if (len(m_Blocks) == 0): # TODO verify all fields are the same not only there!!! m, l, f = checkRequiredFields(block, m_genesisSet[0], [], False) if (len(m) == 0): # TODO revert if loading failed!?!?! m_Blocks.append(block) continue else: ret = self.c_blockchainHandler.verifyThenAddBlock(block) if (len(ret) > 0): # TODO revert if loading failed!?!?! return errMsg(ret) m_pendingTX.update(sysIn['m_pendingTX']) m_BufferMinerCandidates.update(sysIn['m_BufferMinerCandidates']) m_stats.update(sysIn['m_stats'])
def mining_submitBlock(): linkInfo = {} try: values = request.get_json() except Exception: return errMsg("JSON not decodable") return c_MainIntf.nodeSpecificPOST(request.path, linkInfo, values, request)
def peers_connect(): try: values = request.get_json() pt = request.path return c_peer.peersConnect(request.host, values, "dis" not in pt) except Exception: return errMsg("JSON not decodeable")
def nodeSpecificPOSTNow(self, url, linkInfo, json, request): # linkInfo is a json object containing the information from the URL if url == "/genFaucet": return c_genesisInterface.genFaucet(json) elif url == "/useTX": return c_genesisInterface.useTX(json) elif url == "/genTX": return c_genesisInterface.genTX(json) elif url == "/genGX": return c_genesisInterface.genGX(json) elif url == "/updGX": return c_genesisInterface.updGX(json) elif url == "/viewGX": return c_genesisInterface.viewGX(json) elif url == "/setID": return c_genesisInterface.setID(json) #json contains the json object submitted during the POST response = { 'NodeType': m_info['type'], 'info': "This API is not (yet) implemented...." } ## put your logic here and create the reply as next line return errMsg(response)
def nodeSpecificPOSTNow(self, url, linkInfo, json, request): if url == "/wallet/transfer": mes = c_faucetInterface.checkLimit(json) if len(mes) > 0: return errMsg(mes) return wall.walletInterface.nodeSpecificPOSTNow( self, url, linkInfo, json, request)
def handleChainBackTracking(self, peer): try: ret = self.handleBackTracking(peer) except Exception: d("Major issue in backtracking, stopped back tracking") ret = errMsg("Exception raised, invalid peer claim") m_cfg['checkingChain'] = False return ret
def genTX(self, data): ret = self.checkTX(data) if len(ret) > 0: return errMsg(ret) m_data['TXList'].append(data) return setOK( str(len(m_data['TXList'])) + " TXs registered. Most recent type: creating TX")
def receivedNewTransaction(trans, share): # Checks for missing / invalid fields / invalid field values colErr = verifyBasicTX( trans, False, m_transaction) # such can never be coinbase, so False! if colErr == "": trx = deepcopy(trans) passOn = deepcopy(trans) del trx["senderSignature"] # this must be excluded from the hash hash = sha256ToHex(m_transaction_order, trx) #TODO Validates the transaction public key, validates the signature trans["transactionDataHash"] = hash # Checks for collisions -> duplicated transactions are skipped if hash in m_pendingTX: return errMsg("TX is duplicate of in pending TX") if isUniqueTXInBlocks(hash) is False: return errMsg("TX is duplicate of TX in existing block") if ('transferSuccessful' not in trans) or (trans['transferSuccessful'] is True): tmpBal = getBalance(trans['from']) if tmpBal['confirmedBalance'] + tmpBal['pendingBalance'] < trans[ 'value'] + trans['fee']: return errMsg("Not enough balance") # Puts the transaction in the "pending transactions" pool m_pendingTX.update({trans['transactionDataHash']: deepcopy(trans)}) if "transferSuccessful" not in trans: trans["transferSuccessful"] = True trans["minedInBlockIndex"] = len(m_Blocks) m_candidateBlock['transactions'].append(deepcopy(trans)) m_info['pendingTransactions'] = len(m_pendingTX) response = {"transactionDataHash": hash} m_BufferMinerCandidates.clear() if share is True: # Sends the transaction to all peer nodes through the REST API # It goes from peer to peer until it reaches the entire network #TODO do we still need this 'fromPeer'? c_peer.sendAsynchPOSTToPeers("/transactions/send", passOn) return setOK(response, 201) #201 as per slide 38 return # nothing returned, nothing sent return errMsg(colErr)
def getBlockHash(self, params): try: hfrom = +params['from'] hto = +params['to'] hlength = +params['cnt'] if (hfrom < 0) or (hfrom >= len(m_Blocks)) or (hto < hfrom) or \ (hlength < 0) or (hlength > len(defHash)): return errMsg("Inconsistent request") repl = [] if hlength == 0: hlength = len(defHash) for x in range(hfrom, hto+1): if x >= len(m_Blocks): break repl.append([x, m_Blocks[x]['blockHash'][0:hlength]]) return setOK(repl) except Exception: return errMsg("Invalid parameters")
def getAllKeys(self, params): wal = 'unidentified' try: wal = params['wallet'] pattern = re.compile(regexWallet) if not pattern.match(wal): return errMsg("Invalid wallet name.") cmd = "SELECT" sel = False if "py" in params['sel']: cmd = cmd + " 'pubkey:' || pubKey," sel = True if "ay" in params['sel']: cmd = cmd + " 'addr:' || address," sel = True if "ny" in params['sel']: cmd = cmd + " 'name:' || KName" sel = True if sel is False: cmd = cmd + " pubKey, address, KName" else: if cmd.endswith(","): cmd = cmd[0:-1] cmd = cmd + " FROM Wallet WHERE WName='" + wal + "'" ret = project.classes.c_walletInterface.doSelect(cmd) repl = [] for k in ret: if k.startswith("addr"): repl.append(k) else: if k[-1] == "#": repl.append(k + "no") else: repl.append(k[0:k.rindex("#")] + "#yes") return setOK({"keyList": repl}) except Exception: return errMsg("Collecting keys for wallet " + wal + " failed.")
def receivedBlockNotificationFromPeer(self, blockInfo): #TODO do we want to check if NodeUrl and real sender are identical?? try: m, l, f = checkRequiredFields(blockInfo, m_informsPeerNewBlock, [], False) if (len(m) == 0) and (l == 0): return self.checkChainSituation('notification', blockInfo) except Exception: i=0 return errMsg("Invalid block structure")
def visualRelease(idx): try: found = False rel = {} for item in m_Delay: if ('releaseID' in item) and (item['releaseID'] == idx): rel = item found = True break if found is True: m_Delay.remove(rel) if rel['asynchPOST'] is True: requests.post(url=rel['url'], json=rel['json'], headers={'accept': 'application/json'}) return setOK(rel) return errMsg("Unexpected Release for " + str(id)) except Exception: print("visualRelease Failed") return errMsg("Request failed")
def getAllTX(self, params): try: user = params['user'] typep = int(params['type']) return self.filterTX( typep, self.doSelect( "SELECT address, KName, pubKey FROM Wallet WHERE User='******'")) except Exception: return errMsg("Seems to have peer issues....")
def peersConnect(self, source, values, isConnect): m, l, f = checkRequiredFields(['peerUrl'], values, [], False) if len(m) > 0: return errMsg("Missing field 'peerUrl' ") url = values['peerUrl'] newURL = getValidURL(url, True) if newURL == "": return errMsg("Invalid URL: " + url) if isConnect: err = self.addPeerOption(url, source) if len(err) == 0: return setOK("Connection to peer registered") if err.startswith("Already connected"): return errMsg("Already connected to peer: " + url, 409) else: err = self.removePeerOption(url) if len(err) == 0: return setOK("Connection to peer removed") return errMsg(err)
def getTXForAddress(self, address): #TODO move to models reply = {"address": "undefined", "transactions": []} if (len(address) != len(defAdr)): errMsg("Inavlid Address Len") response = self.getPendTXByAddress( address ) #order not clearly defined in slide29, pending more important # if a given hash is found already, no other exists, else keep searching #there is no pending, so return all # if (len(hash) == 0) or (len(response) == 0): response.extend(self.getConfirmedTXByAddress(address)) if (len(response) > 0): reply['address'] = address #we must sort the list by date time in ascending order reply['transactions'] = sorted(response, key=lambda k: k['dateCreated']) return setOK(reply) return errMsg("No transactions found for: " + address, 404)
def visualCfg(): try: values = request.get_json() if values['active'] is True: pattern = re.compile(values['pattern']) m_visualCFG['active'] = True m_visualCFG['pattern'] = pattern return setOK(values) m_visualCFG['active'] = False m_Delay.clear() return setOK("Tracking switched off") except Exception: return errMsg("JSON not decodeable or missing item")
def nodeSpecificGET(self, url, linkInfo): ret = { 'NodeType': m_info['type'], 'info': "This URL/API is not available/broken" } try: if self.permittedURLGET(url) is False: return errMsg("This URL/API is invalid or not available. " + url) if "chainInit" not in m_cfg: m_cfg['chainInit'] = False # backward compatible maxWait = 12 while (len(m_isPOST) > 0) or (m_cfg['chainInit'] is True): if url == "/info": # this is needed for peers to cross reference each other break if url.startswith( "/blockBalances" ): # this is needed for peers to cross reference each other break if self.delay(url, 1) is False: break # for some reason we decide to ignore the lock maxWait = maxWait - 1 if maxWait < 0: print( "Console maxwait for chain update reached, just go ahead now ...." ) break m_simpleLock.append(url) if isBCNode(): ret = self.c_blockInterface.nodeSpecificGETNow(url, linkInfo) elif isWallet(): ret = self.c_walletInterface.nodeSpecificGETNow(url, linkInfo) elif isFaucet(): ret = self.c_faucetInterface.nodeSpecificGETNow(url, linkInfo) elif isGenesis(): ret = self.c_genesisInterface.nodeSpecificGETNow(url, linkInfo) except Exception: d("*********************************************") d("*********************************************") print("GET exception caught, isPoststack " + str(len(m_isPOST))) d("*********************************************") d("*********************************************") if url in m_simpleLock: m_simpleLock.remove( url ) # maybe need to check for being there, then need to add random to URL return ret
def nodeSpecificGETNow(self, url, linkInfo): urlID = url[1:5] if url == "/viewGX": return c_genesisInterface.viewGX() #return send() # identify your url and then proceed #linkInfo is a json object containing the information from the URL response = { 'NodeType': m_info['type'], 'info': "This API is not yet implemented....", 'requestedUrl': url, 'linkInfo': linkInfo } ## put your logic here and create the reply as next line return errMsg(response)
def getWalletBalance(self, params): try: wal = params['wallet'] user = params['user'] bal = deepcopy(m_balanceData) for key in self.doSelect( "SELECT address FROM Wallet WHERE WName='" + wal + "' AND User='******'"): val, respCode = self.collectKeyBalance(key) if respCode == 200: self.sumBalance(bal, val) w_cfg["lastBal"] = "" + str(bal['confirmedBalance']) + "/ " + str( bal['pendingBalance']) return setOK(bal) except Exception: return errMsg("Seems to have peer issues....")
def nodeSpecificGETNow(self, url, linkInfo): urlID = url[1:5] #if (urlID == 'send'): # return send() if urlID == 'info': infow = { 'about': m_info['about'], 'database': m_db['DATABASE'], 'chainID': m_info['chainId'], 'nodeUrl': m_info['nodeUrl'], 'nodeId': m_info['nodeId'] } return setOK(infow) if urlID == 'wall': if url.startswith("/wallet/list/wallet"): return c_walletInterface.getAllWallets(linkInfo['user']) elif url.startswith("/wallet/list/keys/s"): return c_walletInterface.getAllKeys(linkInfo) elif url.startswith("/wallet/list/balance"): return c_walletInterface.getKeyBalance(linkInfo) elif url.startswith("/wallet/list/allbalances"): return c_walletInterface.getAllBalance(linkInfo) elif url.startswith("/wallet/list/allbalance"): return c_walletInterface.getWalletBalance(linkInfo) elif url.startswith("/wallet/list/allkeybalance"): return c_walletInterface.getWalletKeyBalance(linkInfo) elif url.startswith("/wallet/list/allTXs/"): return c_walletInterface.getAllTX(linkInfo) elif url.startswith("/wallet/list/allTX/"): return c_walletInterface.getWalletTX(linkInfo) if urlID == 'addr': return setOK(linkInfo) #return send() # identify your url and then proceed #linkInfo is a json object containing the information from the URL response = { 'NodeType': m_info['type'], 'info': "This API is not yet implemented....", 'requestedUrl': url, 'linkInfo': linkInfo } ## put your logic here and create the reply as next line return errMsg(response)
def getWalletKeyBalance(self, params): try: wal = params['wallet'] user = params['user'] bal = {} for addr in self.doSelect( "SELECT address FROM Wallet WHERE WName='" + wal + "' AND User='******'"): val, respCode = self.collectKeyBalance(addr) if respCode == 200: bal2 = deepcopy(m_balanceData) bal2['confirmedBalance'] = val['confirmedBalance'] bal2['pendingBalance'] = val['pendingBalance'] bal[addr] = bal2 return setOK(bal) except Exception: return errMsg("Seems to have peer issues....")