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 getCtrprtyPubkey(self, c): addr = self.buyer if c else self.seller msg = self.contract.getContractTextOrdered() for sig in self.contract.signatures.values(): pub = multisig.ecdsa_recover(msg, sig) if addr == multisig.pubtoaddr(pub): shared.debug(2, ["Got address", addr, "for pubkey,", pub]) return pub
def receiveContractCNE(self, msg): '''When receiving a contract, first check it's signed and throw it out if not. Otherwise, store it in the list of contracts that have been proposed by possible counterparties We can choose to accept at anytime, within the process/session. However we will not persist contract 'suggestions' across sessions. ''' allContractDetails = ':'.join(msg.split(':')[1:]).split('|') contractDetails = allContractDetails[0] #the contract is in json; need to change it to a Contract object contractDetailsDict = json.loads(contractDetails) tmpContract = Contract.Contract(contractDetailsDict) ca = tmpContract.getCounterparty(self.uniqID()) if not ca: return 'Contract invalid: does not contain this identity' for s in allContractDetails[1:]: ad = multisig.pubtoaddr( multisig.ecdsa_recover(tmpContract.getContractTextOrdered(), s)) shared.debug(2, ["\n recovery produced this address: ", ad, "\n"]) tmpContract.signatures[ad] = s #now the temporary contract object is fully populated; #we can check the signatures match the IDs in the contract for k, v in tmpContract.signatures.iteritems(): if k not in [ tmpContract.text['Buyer BTC Address'], tmpContract.text['Seller BTC Address'] ]: shared.debug(1, ['Error: signature', v, 'from', k, 'was invalid']) return 'Invalid contract signature' self.contractLock.acquire() try: #note that this represents an override for #repeated sending of contracts; one cp can only #be suggesting one contract at a time self.pendingContracts[ca] = tmpContract finally: self.contractLock.release() #if the contract is already signed by me AND ctrprty, send it to escrow if len(tmpContract.signatures.keys()) > 1: self.contractLock.acquire() try: self.workingContract = tmpContract #wipe the pending contract list; we are only #interested in the live contract now self.pendingContracts = {} finally: self.contractLock.release() return 'Signed contract successfully received from counterparty: ' + ca
def receiveContractCNE(self, msg): '''acting as CNE, the escrow can receive a doubly-signed contract at any time from any party. After verifying that the signatures are valid, and that the deposits are specified correctly in the contract, the address for deposits is reported and the contract signed for the third time by the escrow. Messages sent to both parties giving them a deadline for deposit. ''' sender = msg[0].split('.')[1] #this special message is delimited by | allContractDetails = ':'.join(msg[1].split(':')[1:]).split('|') #the contract is in json format contractDetails = allContractDetails[0] tmpContract = Contract.Contract(json.loads(contractDetails)) pubs = {} for s in allContractDetails[1:]: tmpPub = multisig.ecdsa_recover( tmpContract.getContractTextOrdered(), s) ad = multisig.pubtoaddr(tmpPub) shared.debug(2, ["\n recovery produced this address: ", ad, "\n"]) tmpContract.signatures[ad] = s #store the pubkeys for later use pubs[ad] = tmpPub #immediately check for 2 signatures; otherwise dump immediately if len(allContractDetails) != 3: return (False, 'Not a valid and fully signed contract, ignoring', tmpContract) #now the temporary contract object is fully populated; #we can check the signatures match the IDs in the contract for k, v in tmpContract.signatures.iteritems(): if k not in [ tmpContract.text['Buyer BTC Address'], tmpContract.text['Seller BTC Address'] ]: shared.debug(1, ['Error: signature', v, 'from', k, 'was invalid']) return (False, 'Invalid contract signature', tmpContract) #need to check that the proposed deposits follow the business rules verdict, reason = self.checkBusinessRulesCNE(tmpContract) if not verdict: return (verdict, reason, tmpContract) #removed for now. #now we're happy that the contract is valid we build the dep multisig #multisig.initialise(g("Escrow","escrow_pubkey"),g("Directories","escrow_base_dir")) #for a,p in pubs.iteritems(): #multisig.store_share(p,a) #msigaddr, mscript = multisig.create_multisig_address(*pubs.keys()) return (True, multisig.pubtoaddr(g("Escrow", "escrow_pubkey")), tmpContract)
def receiveContractCNE(self,msg): '''When receiving a contract, first check it's signed and throw it out if not. Otherwise, store it in the list of contracts that have been proposed by possible counterparties We can choose to accept at anytime, within the process/session. However we will not persist contract 'suggestions' across sessions. ''' allContractDetails = ':'.join(msg.split(':')[1:]).split('|') contractDetails = allContractDetails[0] #the contract is in json; need to change it to a Contract object contractDetailsDict = json.loads(contractDetails) tmpContract = Contract.Contract(contractDetailsDict) ca = tmpContract.getCounterparty(self.uniqID()) if not ca: return 'Contract invalid: does not contain this identity' for s in allContractDetails[1:]: ad = multisig.pubtoaddr(multisig.ecdsa_recover(tmpContract.getContractTextOrdered(),s)) shared.debug(2,["\n recovery produced this address: ",ad,"\n"]) tmpContract.signatures[ad]=s #now the temporary contract object is fully populated; #we can check the signatures match the IDs in the contract for k,v in tmpContract.signatures.iteritems(): if k not in [tmpContract.text['Buyer BTC Address'],tmpContract.text['Seller BTC Address']]: shared.debug(1,['Error: signature',v,'from',k,'was invalid']) return 'Invalid contract signature' self.contractLock.acquire() try: #note that this represents an override for #repeated sending of contracts; one cp can only #be suggesting one contract at a time self.pendingContracts[ca] = tmpContract finally: self.contractLock.release() #if the contract is already signed by me AND ctrprty, send it to escrow if len(tmpContract.signatures.keys())>1: self.contractLock.acquire() try: self.workingContract = tmpContract #wipe the pending contract list; we are only #interested in the live contract now self.pendingContracts = {} finally: self.contractLock.release() return 'Signed contract successfully received from counterparty: '+ca
def receiveContractCNE(self,msg): '''acting as CNE, the escrow can receive a doubly-signed contract at any time from any party. After verifying that the signatures are valid, and that the deposits are specified correctly in the contract, the address for deposits is reported and the contract signed for the third time by the escrow. Messages sent to both parties giving them a deadline for deposit. ''' sender = msg[0].split('.')[1] #this special message is delimited by | allContractDetails = ':'.join(msg[1].split(':')[1:]).split('|') #the contract is in json format contractDetails = allContractDetails[0] tmpContract = Contract.Contract(json.loads(contractDetails)) pubs = {} for s in allContractDetails[1:]: tmpPub = multisig.ecdsa_recover(tmpContract.getContractTextOrdered(),s) ad = multisig.pubtoaddr(tmpPub) shared.debug(2,["\n recovery produced this address: ",ad,"\n"]) tmpContract.signatures[ad]=s #store the pubkeys for later use pubs[ad]= tmpPub #immediately check for 2 signatures; otherwise dump immediately if len(allContractDetails) != 3: return (False,'Not a valid and fully signed contract, ignoring',tmpContract) #now the temporary contract object is fully populated; #we can check the signatures match the IDs in the contract for k,v in tmpContract.signatures.iteritems(): if k not in [tmpContract.text['Buyer BTC Address'],tmpContract.text['Seller BTC Address']]: shared.debug(1,['Error: signature',v,'from',k,'was invalid']) return (False,'Invalid contract signature',tmpContract) #need to check that the proposed deposits follow the business rules verdict,reason = self.checkBusinessRulesCNE(tmpContract) if not verdict: return (verdict, reason,tmpContract) #removed for now. #now we're happy that the contract is valid we build the dep multisig #multisig.initialise(g("Escrow","escrow_pubkey"),g("Directories","escrow_base_dir")) #for a,p in pubs.iteritems(): #multisig.store_share(p,a) #msigaddr, mscript = multisig.create_multisig_address(*pubs.keys()) return (True,multisig.pubtoaddr(g("Escrow","escrow_pubkey")),tmpContract)
def processNewTxRE(self,msg): txHash,sender = msg[0].split('.') txString,buyerSig,sellerSig,escrowSig,btcTxhash = \ ':'.join(msg[1].split(':')[1:]).split('|') print txString print buyerSig print sellerSig print btcTxhash #shared.debug(2,["We received a transaction string:",txString]) #instantiate the transaction tx = pickle.loads(txString) if tx.uniqID() != txHash: shared.debug(0,["Alert: transaction object passed with inconsistent hash, given",txHash,"should be:",tx.uniqID()]) self.sendMessage('RE_CNE_TX_REJECT_RECEIPT:',recipientID='CNE'+sender,txID=tx.uniqID()) return None #initiate the new multisig address for this transaction tx.msigAddr,tx.mscript = self.getMultisigAddress(tx,g("Escrow","escrow_pubkey")) #allow us to keep track of where the deposits are (fees also collected here) tx.depositHash = btcTxhash #permanent record of identity of CNE tx.CNE = sender #validate the signatures for i,j in zip([buyerSig,sellerSig,escrowSig],[tx.buyer,tx.seller,sender]): testaddress = multisig.pubtoaddr(multisig.ecdsa_recover(tx.contract.getContractTextOrdered(),i)) shared.debug(4,["Recovery produced this address:",multisig.pubtoaddr(testaddress)]) if not testaddress == j: shared.debug(0,["Alert: this transaction is not correctly signed by",j,"- ignoring"]) self.sendMessage('RE_CNE_TX_REJECT_RECEIPT:',recipientID='CNE'+sender,txID=tx.uniqID()) return None else: shared.debug(1,["Correct signature from:",j]) #add the transaction to the persistent store #at this point, a valid transaction has been initialised but not funded in any way. self.transactionUpdate(tx=tx,new_state=400) #send back confirmation message (on THIS message queue) self.sendMessage('RE_CNE_TX_CONFIRM_RECEIPT:',recipientID='CNE'+sender,txID=tx.uniqID())
def processNewTxRE(self, msg): txHash, sender = msg[0].split('.') txString,buyerSig,sellerSig,escrowSig,btcTxhash = \ ':'.join(msg[1].split(':')[1:]).split('|') print txString print buyerSig print sellerSig print btcTxhash #shared.debug(2,["We received a transaction string:",txString]) #instantiate the transaction tx = pickle.loads(txString) if tx.uniqID() != txHash: shared.debug(0, [ "Alert: transaction object passed with inconsistent hash, given", txHash, "should be:", tx.uniqID() ]) self.sendMessage('RE_CNE_TX_REJECT_RECEIPT:', recipientID='CNE' + sender, txID=tx.uniqID()) return None #initiate the new multisig address for this transaction tx.msigAddr, tx.mscript = self.getMultisigAddress( tx, g("Escrow", "escrow_pubkey")) #allow us to keep track of where the deposits are (fees also collected here) tx.depositHash = btcTxhash #permanent record of identity of CNE tx.CNE = sender #validate the signatures for i, j in zip([buyerSig, sellerSig, escrowSig], [tx.buyer, tx.seller, sender]): testaddress = multisig.pubtoaddr( multisig.ecdsa_recover(tx.contract.getContractTextOrdered(), i)) shared.debug(4, [ "Recovery produced this address:", multisig.pubtoaddr(testaddress) ]) if not testaddress == j: shared.debug(0, [ "Alert: this transaction is not correctly signed by", j, "- ignoring" ]) self.sendMessage('RE_CNE_TX_REJECT_RECEIPT:', recipientID='CNE' + sender, txID=tx.uniqID()) return None else: shared.debug(1, ["Correct signature from:", j]) #add the transaction to the persistent store #at this point, a valid transaction has been initialised but not funded in any way. self.transactionUpdate(tx=tx, new_state=400) #send back confirmation message (on THIS message queue) self.sendMessage('RE_CNE_TX_CONFIRM_RECEIPT:', recipientID='CNE' + sender, txID=tx.uniqID())