def sendAdjudicatorCollateralSigningRequest(myself,identityInfo,collateralAmount): msigaddr,mscript = myself.createCollateralMultisig() if not msigaddr or not mscript: shared.debug(0,["Critical error: cannot create a multisignature address."]) return False mypub,mypriv = multisig.getKeysFromUniqueID(myself.uniqID()) shared.debug(0,["You have created this multisig address:",msigaddr]) shared.debug(0,["Please wait while your request is sent out to the pool."]) #broadcast #TODO this is slow but ..? for i,e in enumerate(getEscrowList()): shared.config.set("Escrow","escrow_id",value=e[0]) shared.config.set("Escrow","escrow_pubkey",value=e[2]) shared.config.set("Escrow","escrow_host",value=e[1]) shared.debug(2,["Set the escrow to host:",g("Escrow","escrow_host"),"id:",g("Escrow","escrow_id"),"pubkey:",g("Escrow","escrow_pubkey")]) #Msg.closeConnection() time.sleep(1) Msg.instantiateConnection() myself.sendMessage('ADJUDICATOR_APPLICATION:'+'|'.join([msigaddr,mypub,identityInfo])) shared.debug(0,["Your application has been sent to all pool members. Please wait for their response."]) return True
def __init__(self, basedir, btcaddress): super(EscrowAgent, self).__init__(basedir=basedir, btcadd=btcaddress) shared.debug(1,[\ "instantiating an escrow agent, listening for messages"]) #messaging server should always be local for escrow self.host = '127.0.0.1' #log in to message queue server Msg.instantiateConnection(un=g("Agent", "agent_rabbitmq_user"), pw=g("Agent", "agent_rabbitmq_pass")) #hardcoded for testing TODO self.escrowID = btcaddress #self.superID = g("Escrow","super_id") #get the public list of escrows for propagation to RE self.escrowList = self.getEscrowList() #this needs to be persisted as it contains #state information - in order to accept requests involving two parties, #the escrow needs to keep a record of earlier requests downloaded #from the MQ. The format is a list of lists, each inner list having #a key,message pair [k,m] self.requestStore = [] d = os.path.join(g("Directories", "escrow_base_dir"), "multisig_store") p = g("Escrow", "escrow_pubkey") #initialise multisig multisig.initialise(p, d)
def __init__(self,basedir,btcaddress): super(EscrowAgent,self).__init__(basedir=basedir, btcadd=btcaddress) shared.debug(1,[\ "instantiating an escrow agent, listening for messages"]) #messaging server should always be local for escrow self.host='127.0.0.1' #log in to message queue server Msg.instantiateConnection(un=g("Agent","agent_rabbitmq_user"),pw=g("Agent","agent_rabbitmq_pass")) #hardcoded for testing TODO self.escrowID=btcaddress #self.superID = g("Escrow","super_id") #get the public list of escrows for propagation to RE self.escrowList = self.getEscrowList() #this needs to be persisted as it contains #state information - in order to accept requests involving two parties, #the escrow needs to keep a record of earlier requests downloaded #from the MQ. The format is a list of lists, each inner list having #a key,message pair [k,m] self.requestStore=[] d = os.path.join(g("Directories","escrow_base_dir"),"multisig_store") p = g("Escrow","escrow_pubkey") #initialise multisig multisig.initialise(p,d)
def changeEscrow(specific=None): shared.debug(0, ["Current escrow:", g("Escrow", "escrow_id")]) if specific: c = specific else: for i, e in enumerate(getEscrowList()): print "[" + str(i + 1) + "] " + e[0] c = shared.get_validated_input("Choose an escrow", int) if c not in range(1, len(escrowList) + 1): shared.debug(0, ["Invalid choice from escrow list"]) else: #rewrite the settings file and reset the escrow #TODO: persist changes to config file shared.config.set("Escrow", "escrow_id", value=escrowList[c - 1][0]) shared.config.set("Escrow", "escrow_pubkey", value=escrowList[c - 1][2]) shared.config.set("Escrow", "escrow_host", value=escrowList[c - 1][1]) shared.debug(2, [ "Set the escrow to host:", g("Escrow", "escrow_host"), "id:", g("Escrow", "escrow_id"), "pubkey:", g("Escrow", "escrow_pubkey") ]) Msg.closeConnection() time.sleep(1) Msg.instantiateConnection(un='client1', pw='client1', chanIndex=0)
def getSingleMessage(self,timeout=1,chanIndex=0,prefix=None,external=False): qname = prefix+self.uniqID() if prefix else self.uniqID() msg = Msg.getSingleMessage(qname,timeout,chanIndex,external) if not msg: shared.debug(5,["Message layer returned none"]) return None #all messages must be verified shared.logToFile(g("Directories","agent_base_dir"),"Signed message received:"+msg.keys()[0]+':'+msg.values()[0]+ " at time:"+str(time.time())) sendingID = msg.keys()[0].split('.')[1] #retrieve pubkey msgInner = ';'.join(':'.join(msg.values()[0].split(':')[1:]).split(';')[:-1]) sig = ':'.join(msg.values()[0].split(':')[1:]).split(';')[-1] addr = multisig.pubtoaddr(multisig.ecdsa_recover(msgInner,sig)) if not addr == sendingID: #don't add anything but failure to message to prevent leaks shared.debug(0,["Verification failure",sendingID,chanIndex]) self.sendMessage({msg.keys()[0]:'VERIFICATION_FAILED:'},sendingID,chanIndex) return None else: #having checked the message signature, dispose of it v = ';'.join(msg.values()[0].split(';')[:-1]) msg = {msg.keys()[0]:v} shared.debug(4,["Returning this message:",msg]) return msg
def getSingleMessage(self, timeout=1, chanIndex=0, prefix=None, external=False): qname = prefix + self.uniqID() if prefix else self.uniqID() msg = Msg.getSingleMessage(qname, timeout, chanIndex, external) if not msg: shared.debug(5, ["Message layer returned none"]) return None #all messages must be verified shared.logToFile( g("Directories", "agent_base_dir"), "Signed message received:" + msg.keys()[0] + ':' + msg.values()[0] + " at time:" + str(time.time())) sendingID = msg.keys()[0].split('.')[1] #retrieve pubkey msgInner = ';'.join(':'.join( msg.values()[0].split(':')[1:]).split(';')[:-1]) sig = ':'.join(msg.values()[0].split(':')[1:]).split(';')[-1] addr = multisig.pubtoaddr(multisig.ecdsa_recover(msgInner, sig)) if not addr == sendingID: #don't add anything but failure to message to prevent leaks shared.debug(0, ["Verification failure", sendingID, chanIndex]) self.sendMessage({msg.keys()[0]: 'VERIFICATION_FAILED:'}, sendingID, chanIndex) return None else: #having checked the message signature, dispose of it v = ';'.join(msg.values()[0].split(';')[:-1]) msg = {msg.keys()[0]: v} shared.debug(4, ["Returning this message:", msg]) return msg
def sendMessage(self,msg,recipientID='CNE',txID=None,chanIndex=0): '''wrapper function for sending messages to a counterparty. Not all messages have an associated transactionid, so that is allowed to be null and is replaced by '0'. If recipientID is not set, it defaults to the active CNE. A signature of the value of the single dict entry is appended to the value after the last ';' as identity authorization. ''' if not txID: txID='0' if recipientID in ['CNE','RE']: #TODO fix recipientID = recipientID+g("Escrow","escrow_id") text,sig = multisig.signText(self.uniqID(),':'.join(msg.split(':')[1:])) newMsg = {} newMsg[txID+'.'+self.uniqID()]=msg+';'+sig Msg.sendMessages(newMsg,recipientID,chanIndex)
def sendMessage(self, msg, recipientID='CNE', txID=None, chanIndex=0): '''wrapper function for sending messages to a counterparty. Not all messages have an associated transactionid, so that is allowed to be null and is replaced by '0'. If recipientID is not set, it defaults to the active CNE. A signature of the value of the single dict entry is appended to the value after the last ';' as identity authorization. ''' if not txID: txID = '0' if recipientID in ['CNE', 'RE']: #TODO fix recipientID = recipientID + g("Escrow", "escrow_id") text, sig = multisig.signText(self.uniqID(), ':'.join(msg.split(':')[1:])) newMsg = {} newMsg[txID + '.' + self.uniqID()] = msg + ';' + sig Msg.sendMessages(newMsg, recipientID, chanIndex)
def changeEscrow(specific=None): shared.debug(0,["Current escrow:",g("Escrow","escrow_id")]) if specific: c = specific else: for i,e in enumerate(getEscrowList()): print "["+str(i+1)+"] "+e[0] c = shared.get_validated_input("Choose an escrow",int) if c not in range(1,len(escrowList)+1): shared.debug(0,["Invalid choice from escrow list"]) else: #rewrite the settings file and reset the escrow #TODO: persist changes to config file shared.config.set("Escrow","escrow_id",value=escrowList[c-1][0]) shared.config.set("Escrow","escrow_pubkey",value=escrowList[c-1][2]) shared.config.set("Escrow","escrow_host",value=escrowList[c-1][1]) shared.debug(2,["Set the escrow to host:",g("Escrow","escrow_host"),"id:",g("Escrow","escrow_id"),"pubkey:",g("Escrow","escrow_pubkey")]) Msg.closeConnection() time.sleep(1) Msg.instantiateConnection(un='client1',pw='client1',chanIndex=0)
def sendAdjudicatorCollateralSigningRequest(myself, identityInfo, collateralAmount): msigaddr, mscript = myself.createCollateralMultisig() if not msigaddr or not mscript: shared.debug( 0, ["Critical error: cannot create a multisignature address."]) return False mypub, mypriv = multisig.getKeysFromUniqueID(myself.uniqID()) shared.debug(0, ["You have created this multisig address:", msigaddr]) shared.debug(0, ["Please wait while your request is sent out to the pool."]) #broadcast #TODO this is slow but ..? for i, e in enumerate(getEscrowList()): shared.config.set("Escrow", "escrow_id", value=e[0]) shared.config.set("Escrow", "escrow_pubkey", value=e[2]) shared.config.set("Escrow", "escrow_host", value=e[1]) shared.debug(2, [ "Set the escrow to host:", g("Escrow", "escrow_host"), "id:", g("Escrow", "escrow_id"), "pubkey:", g("Escrow", "escrow_pubkey") ]) #Msg.closeConnection() time.sleep(1) Msg.instantiateConnection() myself.sendMessage('ADJUDICATOR_APPLICATION:' + '|'.join([msigaddr, mypub, identityInfo])) shared.debug(0, [ "Your application has been sent to all pool members. Please wait for their response." ]) return True
def processInboundMessages(self,parentThread): '''messages coming from the "back end" (escrow MQ server) are picked up here. Message syntax will be the same for front end and back end communication, improving intelligibility. Automatic responses to MQ instructions occurs here, whereas anything that needs user input or is purely informational is blindly passed up to the front end.''' #need a connection to an escrow to do anything Msg.instantiateConnection(chanIndex=1) #infinite loop for getting messages while True: time.sleep(1) msg = self.getSingleMessage(chanIndex=1) if not msg: continue for k,m in msg.iteritems(): if 'SELF_SHUTDOWN' in m: break if 'CNE_SIGNED_CONTRACT' in m: response = self.receiveContractCNE(m) #let the front end know we got it etc. self.qFrontEnd.put('CONTRACT RECEIVED:'+response) continue elif 'CNE_CHAT' in m: self.qFrontEnd.put('CHAT RECEIVED:'+k.split('.')[1]+\ ':'+':'.join(m.split(':')[1:])) continue elif 'QUERY_STATUS:' in m: #was it RE or CNE? sender = k.split('.')[1] recipientID = 'RE' if sender.startswith('RE') else 'CNE' requester = m.split(':')[1] self.sendMessage('QUERY_STATUS_RESPONSE:ONLINE,'+requester, recipientID='CNE', chanIndex=1) elif 'CNE_CONTRACT_SUCCESS' in m: escrowAddressConfirmation,contractJSON,escrowSig =\ ':'.join(m.split(':')[1:]).split('|') #validate escrow address if not escrowAddressConfirmation == \ multisig.pubtoaddr(g("Escrow","escrow_pubkey")): raise Exception("Panic! We are talking to the wrong escrow:"\ +escrowAddressConfirmation) testingContract = Contract.Contract(json.loads(contractJSON)) #validate escrow's signature and append to the contract if not multisig.ecdsa_verify(testingContract.getContractTextOrdered(),\ escrowSig,g("Escrow","escrow_pubkey")): raise Exception("Panic! We received an invalid signature from the escrow.") #set this as the working contract in case it isn't if self.workingContract != testingContract: shared.debug(0,["Warning, switched back to the contract as informed by escrow"]) contractLock.acquire() try: self.workingContract = testingContract finally: contractLock.release() self.qFrontEnd.put(m) continue elif 'RE_BANK_SESSION_START_REQUEST' in m: t = self.getTxByID(k.split('.')[0]) if self.uniqID() != t.seller: shared.debug(0,["Error, received bank session request but not seller"]) continue else: self.sendMessage('RE_BANK_SESSION_START_ACCEPTED:',recipientID='RE', txID=t.uniqID(),chanIndex=1) self.startBankingSession(t) continue elif 'RE_BANK_SESSION_START_ACCEPTED' in m or \ 'RE_BANK_SESSION_START_REJECTED' in m: shared.debug(0,["Received an acceptance or rejection, putting to queue",k,m]) self.qFrontEnd.put({k:m}) continue elif 'RE_TRANSACTION_SYNC_RESPONSE' in m or \ 'RE_TRANSACTION_SYNC_COMPLETE' in m: self.qFrontEnd.put({k:m}) continue elif 'RE_BANK_SESSION_ENDED' in m: transaction = self.getTxByID(k.split('.')[0]) if not transaction.getRole(self.uniqID()) == 'seller': shared.debug(0,["Error, bank session end message received but not the seller"]) continue rspns = m.split(':')[1] self.endBankingSession(transaction, rspns) elif 'RE_SSL_KEYS_REQUEST' in m: transaction = self.getTxByID(k.split('.')[0]) else: #catch all for messages which just go to the front end shared.debug(0,["Putting this to the q,",m]) self.qFrontEnd.put(m)
if __name__ == "__main__": #first connect to CNE #code for reading order books and choosing escrow here? myBtcAddress=None myself = None receiverThread=None #Load all necessary configurations: helper_startup.loadconfig(sys.argv[1]) global txRE myEscrow = g("Escrow","escrow_id") d = os.path.join(g("Directories","agent_base_dir"),"multisig_store") p = g("Escrow","escrow_pubkey") #initialise multisig multisig.initialise(p,d) #need a connection to an escrow to do anything Msg.instantiateConnection(un='guest', pw='guest') #read in contract details (for early testing only) contractDetails = {} with open("AppLayer/boilerplate.txt") as fi: lines = fi.readlines() for line in lines: if line.startswith('***'): k,v = line[3:].strip().split(':: ') contractDetails[k]=v boilerplateContract = Contract.Contract(contractDetails) #flag to control which menu to use for contacting which type of escrow #TODO: actual connection switching; could still use EscrowAccessor concept?
if __name__ == "__main__": #first connect to CNE #code for reading order books and choosing escrow here? myBtcAddress = None myself = None receiverThread = None #Load all necessary configurations: helper_startup.loadconfig(sys.argv[1]) global txRE myEscrow = g("Escrow", "escrow_id") d = os.path.join(g("Directories", "agent_base_dir"), "multisig_store") p = g("Escrow", "escrow_pubkey") #initialise multisig multisig.initialise(p, d) #need a connection to an escrow to do anything Msg.instantiateConnection(un='guest', pw='guest') #read in contract details (for early testing only) contractDetails = {} with open("AppLayer/boilerplate.txt") as fi: lines = fi.readlines() for line in lines: if line.startswith('***'): k, v = line[3:].strip().split(':: ') contractDetails[k] = v boilerplateContract = Contract.Contract(contractDetails) #flag to control which menu to use for contacting which type of escrow #TODO: actual connection switching; could still use EscrowAccessor concept? RE = False
#first connect to CNE #code for reading order books and choosing escrow here? myBtcAddress=None myself = None receiverThread=None #Load all necessary configurations: helper_startup.loadconfig(sys.argv[1]) global txRE myEscrow = g("Escrow","escrow_id") d = os.path.join(g("Directories","escrow_base_dir"),"multisig_store") #p = g("Escrow","escrow_pubkey") #initialise multisig #multisig.initialise(p,d) #TODO remove this "initialize" thing multisig.msd=d #need a connection to an escrow to do anything Msg.instantiateConnection() adjudicator = AdjudicatorAgent(d,myEscrow,txStore=False) while True: print """Please choose an option: [1] Read message from queue [2] Choose transaction [3] Adjudicate transaction [4] Review adjudicator applications [5] Exit """ choice = shared.get_validated_input("Enter an integer:",int) if choice==1: msg = adjudicator.getSingleMessage(prefix='ADJ')
def processInboundMessages(self, parentThread): '''messages coming from the "back end" (escrow MQ server) are picked up here. Message syntax will be the same for front end and back end communication, improving intelligibility. Automatic responses to MQ instructions occurs here, whereas anything that needs user input or is purely informational is blindly passed up to the front end.''' #need a connection to an escrow to do anything Msg.instantiateConnection(chanIndex=1) #infinite loop for getting messages while True: time.sleep(1) msg = self.getSingleMessage(chanIndex=1) if not msg: continue for k, m in msg.iteritems(): if 'SELF_SHUTDOWN' in m: break if 'CNE_SIGNED_CONTRACT' in m: response = self.receiveContractCNE(m) #let the front end know we got it etc. self.qFrontEnd.put('CONTRACT RECEIVED:' + response) continue elif 'CNE_CHAT' in m: self.qFrontEnd.put('CHAT RECEIVED:'+k.split('.')[1]+\ ':'+':'.join(m.split(':')[1:])) continue elif 'QUERY_STATUS:' in m: #was it RE or CNE? sender = k.split('.')[1] recipientID = 'RE' if sender.startswith('RE') else 'CNE' requester = m.split(':')[1] self.sendMessage('QUERY_STATUS_RESPONSE:ONLINE,' + requester, recipientID='CNE', chanIndex=1) elif 'CNE_CONTRACT_SUCCESS' in m: escrowAddressConfirmation,contractJSON,escrowSig =\ ':'.join(m.split(':')[1:]).split('|') #validate escrow address if not escrowAddressConfirmation == \ multisig.pubtoaddr(g("Escrow","escrow_pubkey")): raise Exception("Panic! We are talking to the wrong escrow:"\ +escrowAddressConfirmation) testingContract = Contract.Contract( json.loads(contractJSON)) #validate escrow's signature and append to the contract if not multisig.ecdsa_verify(testingContract.getContractTextOrdered(),\ escrowSig,g("Escrow","escrow_pubkey")): raise Exception( "Panic! We received an invalid signature from the escrow." ) #set this as the working contract in case it isn't if self.workingContract != testingContract: shared.debug(0, [ "Warning, switched back to the contract as informed by escrow" ]) contractLock.acquire() try: self.workingContract = testingContract finally: contractLock.release() self.qFrontEnd.put(m) continue elif 'RE_BANK_SESSION_START_REQUEST' in m: t = self.getTxByID(k.split('.')[0]) if self.uniqID() != t.seller: shared.debug(0, [ "Error, received bank session request but not seller" ]) continue else: self.sendMessage('RE_BANK_SESSION_START_ACCEPTED:', recipientID='RE', txID=t.uniqID(), chanIndex=1) self.startBankingSession(t) continue elif 'RE_BANK_SESSION_START_ACCEPTED' in m or \ 'RE_BANK_SESSION_START_REJECTED' in m: shared.debug(0, [ "Received an acceptance or rejection, putting to queue", k, m ]) self.qFrontEnd.put({k: m}) continue elif 'RE_TRANSACTION_SYNC_RESPONSE' in m or \ 'RE_TRANSACTION_SYNC_COMPLETE' in m: self.qFrontEnd.put({k: m}) continue elif 'RE_BANK_SESSION_ENDED' in m: transaction = self.getTxByID(k.split('.')[0]) if not transaction.getRole(self.uniqID()) == 'seller': shared.debug(0, [ "Error, bank session end message received but not the seller" ]) continue rspns = m.split(':')[1] self.endBankingSession(transaction, rspns) elif 'RE_SSL_KEYS_REQUEST' in m: transaction = self.getTxByID(k.split('.')[0]) else: #catch all for messages which just go to the front end shared.debug(0, ["Putting this to the q,", m]) self.qFrontEnd.put(m)
#first connect to CNE #code for reading order books and choosing escrow here? myBtcAddress = None myself = None receiverThread = None #Load all necessary configurations: helper_startup.loadconfig(sys.argv[1]) global txRE myEscrow = g("Escrow", "escrow_id") d = os.path.join(g("Directories", "escrow_base_dir"), "multisig_store") #p = g("Escrow","escrow_pubkey") #initialise multisig #multisig.initialise(p,d) #TODO remove this "initialize" thing multisig.msd = d #need a connection to an escrow to do anything Msg.instantiateConnection() adjudicator = AdjudicatorAgent(d, myEscrow, txStore=False) while True: print """Please choose an option: [1] Read message from queue [2] Choose transaction [3] Adjudicate transaction [4] Review adjudicator applications [5] Exit """ choice = shared.get_validated_input("Enter an integer:", int) if choice == 1: msg = adjudicator.getSingleMessage(prefix='ADJ') print msg