def msg_settleCommit(self, msg): transactionID = settings.hashAlgorithm(msg.token) ret = [] try: payer = self.__getLinkObject(msg.ID) ret += payer.settleCommitIncoming(msg) except LinkNotFound: #Payment is committed, so payer object may already be deleted #Pass: continue with payee side handling and tx removing pass try: tx = self.findTransaction(transactionID=transactionID, payerID=msg.ID) except TransactionNotFound: #Payment is committed, so transaction object may already be deleted #Return here: don't remove non-existing tx return ret try: payee = self.__getLinkObject(tx.payeeID) ret += payee.settleCommitOutgoing(msg) except LinkNotFound: #Payment is committed, so payee object may already be deleted #Pass: continue with removing tx pass #Clean up no-longer-needed transaction: self.transactions.remove(tx) return ret
def settleCommitOutgoing(self, msg): transactionID = settings.hashAlgorithm(msg.token) routeID = self.__makeRouteID(transactionID, msg.isPayerSide) try: c, ci = self.__findChannelWithRoute(routeID) except RouteNotInChannelsException: log.log('No channel found for route; assuming settleCommitOutgoing was already performed, so we skip it.') return [] ret = self.handleChannelOutput( ci, c.settleCommitOutgoing(routeID, msg.token) ) #TODO: add payload msg = copy.deepcopy(msg) msg.ID = self.remoteID return ret + \ [ messages.OutboundMessage(localID=self.localID, message=msg), messages.FilterTimeouts(function = lambda message: \ not( isinstance(message, messages.LinkTimeout_Commit) and message.transactionID == transactionID and message.isPayerSide == msg.isPayerSide and message.ID == self.localID )) ]
def settleCommitIncoming(self, msg): #TODO: process payload transactionID = settings.hashAlgorithm(msg.token) c = self.__findChannelWithTransaction(transactionID) c.settleCommitIncoming(transactionID) return []
def settleCommitIncoming(self, msg): #TODO: process payload transactionID = settings.hashAlgorithm(msg.token) routeID = self.__makeRouteID(transactionID, msg.isPayerSide) c, ci = self.__findChannelWithRoute(routeID) return self.handleChannelOutput( ci, c.settleCommitIncoming(routeID) )
def msg_commit(self, msg): transactionID = settings.hashAlgorithm(msg.token) tx = self.transactions[transactionID] payer = self.__getLinkObject(tx.payerID) payee = self.__getLinkObject(tx.payeeID) ret = payee.commitIncoming(msg) ret += payer.commitOutgoing(msg) ret += payee.settleCommitOutgoing(messages.SettleCommit(token=msg.token)) return ret
def settleCommitOutgoing(self, msg, localID): transactionID = settings.hashAlgorithm(msg.token) try: c = self.__findChannelWithTransaction(transactionID) except TransactionNotInChannelsException: log.log('No channel found for transaction; assuming settleCommitOutgoing was already performed, so we skip it.') return [] c.settleCommitOutgoing(transactionID, msg.token) #TODO: add payload msg = copy.deepcopy(msg) msg.ID = self.remoteID return [messages.OutboundMessage(localID=localID, message=msg)]
def msg_commit(self, msg): transactionID = settings.hashAlgorithm(msg.token) try: tx = self.findTransaction(transactionID=transactionID, payeeID=msg.ID) except TransactionNotFound: log.log('Received a commit message for an unknown transaction. Probably we\'ve already settled, so we ignore this.') return [] payer = self.__getLinkObject(tx.payerID) payee = self.__getLinkObject(tx.payeeID) ret = payee.commitIncoming(msg) ret += payer.commitOutgoing(msg) ret += payee.settleCommitOutgoing(messages.SettleCommit(token=msg.token)) return ret
def msg_commit(self, msg): transactionID = settings.hashAlgorithm(msg.token) try: tx = self.transactions[transactionID] except KeyError: log.log('Received a commit message for an unknown transaction. Probably we\'ve already settled, so we ignore this.') return [] payer = self.__getObject(tx.payerID) payee = self.__getObject(tx.payeeID) ret = payee.commitIncoming(msg) ret += payer.commitOutgoing(msg, tx.payerID) ret += payee.settleCommitOutgoing(messages.SettleCommit(token=msg.token), tx.payeeID) return ret
def __init__(self, context, routingContext, ID, amount, receipt, token, suggestedMeetingPoints): event.Handler.__init__(self, context) self.routingContext = routingContext self.ID = ID self.amount = amount self.receipt = receipt self.token = token self.hash = settings.hashAlgorithm(self.token) self.suggestedMeetingPoints = suggestedMeetingPoints self.__meetingPoint = None #unknown self.__transaction = None self.connection = None self.state = self.states.initial self.__payerHasRoute = False self.__payeeHasRoute = False
def msg_settleCommit(self, msg): #Special case for payee->payer transmission of this message type: if msg.ID == messages.payerLocalID: #Start settling on the payer side route, #no matter what the payee says. msg.isPayerSide = True transactionID = settings.hashAlgorithm(msg.token) ret = [] try: payer = self.__getLinkObject(msg.ID) ret += payer.settleCommitIncoming(msg) except LinkNotFound: log.log('Payer link not found (ignored)') #Payment is committed, so payer object may already be deleted #Pass: continue with payee side handling and tx removing pass try: tx = self.findTransaction( transactionID=transactionID, payerID=msg.ID, isPayerSide=msg.isPayerSide) except TransactionNotFound: log.log('Transaction not found (ignored)') #Payment is committed, so transaction object may already be deleted #Return here: don't remove non-existing tx return ret try: payee = self.__getLinkObject(tx.payeeID) ret += payee.settleCommitOutgoing(msg) except LinkNotFound: log.log('Payee link not found (ignored)') #Payment is committed, so payee object may already be deleted #Pass: continue with removing tx pass #Clean up no-longer-needed transaction: self.transactions.remove(tx) return ret
def msg_requestCommit(self, msg): transactionID = settings.hashAlgorithm(msg.token) try: tx = self.findTransaction( transactionID=transactionID, payeeID=msg.ID, isPayerSide=msg.isPayerSide) except TransactionNotFound: log.log('Received a request commit message for an unknown transaction. Probably we\'ve already settled, so we ignore this.') return [] payer = self.__getLinkObject(tx.payerID) payee = self.__getLinkObject(tx.payeeID) tx.state = transaction.Transaction.states.requestedCommit ret = payee.requestCommitIncoming(msg) ret += payer.requestCommitOutgoing(msg) ret += payee.settleCommitOutgoing( messages.SettleCommit(token=msg.token, isPayerSide=msg.isPayerSide)) return ret
def msg_settleCommit(self, msg): transactionID = settings.hashAlgorithm(msg.token) tx = self.transactions[transactionID] ret = [] try: payer = self.__getLinkObject(tx.payerID) ret += payer.settleCommitIncoming(msg) except LinkNotFound: pass #Payment is committed, so payer object may already be deleted try: payee = self.__getLinkObject(tx.payeeID) ret += payee.settleCommitOutgoing(msg) except LinkNotFound: pass #Payment is committed, so payee object may already be deleted #Clean up no-longer-needed transaction: del self.transactions[transactionID] return ret
def __handleMessage(self, message): #log.log("Link received message: " + repr(str())) 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: log.log("Link received 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.amount) #TODO: exception handling for the above except channel.CheckFail as f: log.log("Route refused by link: " + str(f)) #Send back a cancel immediately #TODO return if message.isPayerSide: self.openTransactions[message.hash] = transaction.Transaction( self.context, self.routingContext, message.amount, message.hash, message.meetingPoint, payerLink=self) else: self.openTransactions[message.hash] = transaction.Transaction( self.context, self.routingContext, message.amount, message.hash, message.meetingPoint, payeeLink=self) #This will start the transaction routing self.openTransactions[message.hash].msg_makeRoute() elif message.__class__ == messages.HaveRoute: log.log("Link received HaveRoute") self.openTransactions[message.value].msg_haveRoute(self) elif message.__class__ == messages.Lock: log.log("Link received Lock") #TODO: get new Bitcoin transaction from message and # pass it to channel #TODO: use multiple channels self.channels[0].lockIncoming(message.value) #TODO: exception handling for the above self.context.sendSignal(None, event.signals.save) self.openTransactions[message.value].msg_lock() elif message.__class__ == messages.Commit: log.log("Link received Commit") token = message.value hash = settings.hashAlgorithm(token) #TODO: get new Bitcoin transaction from message and # pass it to channel #TODO: use multiple channels self.channels[0].commitIncoming(hash) #TODO: exception handling for the above self.context.sendSignal(None, event.signals.save) self.openTransactions[hash].msg_commit(token) #We don't need this anymore: del self.openTransactions[hash] else: log.log("Link received unsupported message: %s" % str(message))
def __init__(self, **kwargs): serializable.Serializable.__init__(self, **kwargs) #This will fail if token is not set (is None). #So, token must always be set for successful construction. self.transactionID = settings.hashAlgorithm(self.token)
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.")