def withdraw(self, channelID): if not self.isConnected(): raise Exception("Not connected") existingIDs = [c.ID for c in self.channels] if channelID not in existingIDs: raise Exception("Channel ID does not exist") channel = self.channels[existingIDs.index(channelID)] msg = channel.makeWithdrawMessage(None) if msg != None: self.connection.sendMessage(msg) self.context.sendSignal(None, event.signals.save)
def __handleMessage(self, message): log.log("Link received message (%s -> %s): %s" % \ (str(self.remoteID), str(self.localID), str(message) )) if message.__class__ == messages.MyURLs: #TODO: check URLs for validity etc. #TODO: support multiple remote URLs (for now, just pick the first) remoteURL = message.getURLs()[0] URL = urlparse(remoteURL) oldRemoteID = self.remoteID oldRemoteURL = self.remoteURL self.remoteID = URL.path[1:] self.remoteURL = remoteURL if oldRemoteID != self.remoteID or oldRemoteURL != self.remoteURL: self.context.sendSignal(None, event.signals.save) elif message.__class__ == messages.MakeRoute: try: #TODO: do all sorts of checks to see if it makes sense to perform #the transaction over this link. #For instance, check the responsiveness of the other side, etc. etc. #This will check whether enough funds are availbale #Note: if we're on the PAYER side of the meeting point, #then we're on the PAYEE side of this link, for this transaction. #TODO: use multiple channels self.channels[0].reserve( not message.isPayerSide, message.hash, message.startTime, message.endTime, message.amount) #TODO: exception handling for the above except channel.CheckFail as f: log.log("Route refused by link: " + str(f)) #Send back a have no route immediately #TODO return if message.isPayerSide: self.openTransactions[message.hash] = transaction.Transaction( self.context, self.routingContext, message.meetingPoint, message.amount, message.hash, message.startTime, message.endTime, payerLink=self) else: self.openTransactions[message.hash] = transaction.Transaction( self.context, self.routingContext, message.meetingPoint, message.amount, message.hash, message.startTime, message.endTime, payeeLink=self) #This will start the transaction routing #Give it our own ID, to prevent routing back to this link. self.openTransactions[message.hash].msg_makeRoute(self.localID) elif message.__class__ == messages.HaveNoRoute: #TODO: use multiple channels tx = self.openTransactions[message.value] self.channels[0].unreserve(tx.isPayerSide, tx.hash) tx.msg_haveNoRoute() elif message.__class__ == messages.HaveRoute: tx = self.openTransactions[message.hash] startTime, endTime = message.startTime, message.endTime if not tx.isPayerSide: #TODO: on payee side, check equality of timestamp values #On the payee side, don't overwrite the values that are #already in the transaction. #Note that these are different from the ones received in the #message. startTime, endTime = tx.startTime, tx.endTime tx.msg_haveRoute(self, startTime, endTime) elif message.__class__ == messages.Lock: #TODO: check that we're the payee side #TODO: use multiple channels self.channels[0].lockIncoming(message) #TODO: exception handling for the above self.context.sendSignal(None, event.signals.save) self.openTransactions[message.hash].msg_lock() elif message.__class__ == messages.RequestCommit: token = message.value hash = settings.hashAlgorithm(token) #If hash is not in openTransactions, then either the commit token #was wrong, or e.g. we already removed the transaction because #we received the token from the payer side. if hash in self.openTransactions: #TODO: check time-out #TODO: check that we're the payer side #TODO: use multiple channels message = self.channels[0].commitOutgoing(hash, token) self.context.sendSignal(None, event.signals.save) self.connection.sendMessage(message) #Now also request a commit on our upstream payer side self.openTransactions[hash].msg_requestCommit(token) #We don't need this anymore: del self.openTransactions[hash] elif message.__class__ == messages.Commit: token = message.token hash = settings.hashAlgorithm(token) #TODO: check that we're the payee side #TODO: use multiple channels self.channels[0].commitIncoming(hash, message) #TODO: exception handling for the above self.context.sendSignal(None, event.signals.save) #If hash is not in openTransactions, then either the commit token #was wrong, or e.g. we already removed the transaction because #we received the token from the payee side. if hash in self.openTransactions: self.openTransactions[hash].msg_commit(token) #We don't need this anymore: del self.openTransactions[hash] elif message.__class__ == messages.Deposit: existingIDs = [c.ID for c in self.channels] if message.isInitial: if message.channelID in existingIDs: log.log("Initial deposit message contains already existing channel ID") #TODO: send refusal reply? elif message.type not in ["multisig"]: log.log("Initial deposit message with unsupported channel type") #TODO: send refusal reply? else: newChannel = multisigchannel.constructFromDepositMessage( self.bitcoind, message) self.channels.append(newChannel) reply = newChannel.makeDepositMessage(message) if reply != None: self.connection.sendMessage(reply) self.context.sendSignal(None, event.signals.save) else: try: channel = self.channels[existingIDs.index(message.channelID)] reply = channel.makeDepositMessage(message) if reply != None: self.connection.sendMessage(reply) self.context.sendSignal(None, event.signals.save) except ValueError: log.log("Follow-up deposit message contains non-existing channel ID") #TODO: send refusal reply? elif message.__class__ == messages.Withdraw: existingIDs = [c.ID for c in self.channels] try: channel = self.channels[existingIDs.index(message.channelID)] reply = channel.makeWithdrawMessage(message) if reply != None: self.connection.sendMessage(reply) self.context.sendSignal(None, event.signals.save) except ValueError: log.log("Withdraw message contains non-existing channel ID") #TODO: send refusal reply? else: log.log("Link: message type is not supported; message ignored.")