def __init__(self, coins, target): from containers import sumCurrencies self.coins = coins self.amount = sumCurrencies(coins) self.target = target import base64 from crypto import _r as Random self.transaction_id = Random.getRandomString(128) self.encoded_transaction_id = base64.b64encode(self.transaction_id) Protocol.__init__(self) self.state = self.initiateHandshake
def __init__(self,coins,target): from containers import sumCurrencies self.coins = coins self.amount = sumCurrencies(coins) self.target = target import base64 from crypto import _r as Random self.transaction_id = Random.getRandomString(128) self.encoded_transaction_id = base64.b64encode(self.transaction_id) Protocol.__init__(self) self.state = self.initiateHandshake
def handleCoins(self,message): import base64 if message.type == 'TOKEN_SPEND': try: encoded_transaction_id, tokens, target = message.data except ValueError: return ProtocolErrorMessage('TS') if not isinstance(encoded_transaction_id, types.StringType): return ProtocolErrorMessage('TS') if not isinstance(tokens, types.ListType): return ProtocolErrorMessage('TS') if not tokens: # We require tokens return ProtocolErrorMessage('TS') for token in tokens: if not isinstance(token, types.ListType): return ProtocolErrorMessage('TS') # Convert transaction_id try: transaction_id = base64.b64decode(encoded_transaction_id) except TypeError: return ProtocolErrorMessage('TS') # Convert the tokens try: tokens = [containers.CurrencyCoin().fromPython(c) for c in tokens] except AttributeError: #FIXME: this is the wrong error, I'm pretty sure return ProtocolErrorMessage('TS') # And now do things from containers import sumCurrencies #be conservative result = Message('TOKEN_REJECT','default') if transaction_id != self.transaction_id: #FIXME: is the a PROTOCOL_ERROR? result = Message('TOKEN_REJECT','Rejected') elif sumCurrencies(tokens) != self.sum: result = Message('TOKEN_REJECT','Rejected') elif target != self.target: result = Message('TOKEN_REJECT','Rejected') elif self.action in ['redeem','exchange','trust']: out = self.wallet.handleIncomingCoins(tokens,self.action,target) if out: result = Message('TOKEN_ACCEPT') self.state = self.goodbye return result elif message.type == 'PROTOCOL_ERROR': pass else: return ProtocolErrorMessage('TokenSendRecipient')
# send the key to the mint. failures.append(mintKey.encodeField('key_identifier')) if failures: self.issuer.dsdb.unlock(transaction_id) return Message('TRANSFER_TOKEN_REJECT', [encoded_transaction_id, 'Blind', 'Invalid key_identifier', []]) # Make sure that we have the same amount of coins as mintings from fraction import Fraction from containers import sumCurrencies total = Fraction('0') for b in blinds: total += b[0].denomination * len(b[1]) if total != sumCurrencies(coins): self.issuer.dsdb.unlock(transaction_id) return Message('TRANSFER_TOKEN_REJECT', [encoded_transaction_id, 'Generic', 'Rejected', []]) # mint them immediately (the only thing we can do right now with the mint) minted = [] from entities import MintError for key, blindlist in blinds: this_set = [] for blind in blindlist: try: signature = self.issuer.mint.signNow(key.key_identifier, blind) except MintError: self.issuer.dsdb.unlock(transaction_id) return Message('TRANSFER_TOKEN_REJECT', [encoded_transaction_id, 'Blind', 'Unable to sign', []]) this_set.append(base64.b64encode(signature))
def sendCoins(self,transport,target,amount=None): """sendCoins sends coins over a transport to a target of a certain amount. We need to be careful to try every possible working combination of coins to to an amount. To test, we muck in the internals of the coin sending to make sure that we get the right coins. (Note: This would be easier if we just had a function to get the coins for an amount.) To test the functionality we steal the transport, and when a message occurs, we steal the tokens directly out of the protocol. This is highly dependant on the internal details of TokenSpendSender and the transport/protocol relationship. >>> class transport: ... from containers import sumCurrencies ... def setProtocol(self, protocol): ... protocol.transport = self ... self.protocol = protocol ... def start(self): pass ... def write(self, info): print sumCurrencies(self.protocol.coins) # Steal the values >>> wallet = Wallet() >>> test = lambda x: wallet.sendCoins(transport(), '', x) >>> from tests import coins >>> from containers import sumCurrencies >>> from fraction import Fraction Okay. Do some simple checks to make sure things work at all >>> wallet.coins = [coins[0][0]] >>> test(Fraction(1)) 1 >>> wallet.coins = [coins[0][0], coins[2][0]] >>> test(Fraction(6)) 6 >>> wallet.coins = [coins[2][0], coins[0][0]] >>> test(Fraction(6)) 6 Okay. Now we'll do some more advanced tests of the system. We start off with a specifically selected group of coins: 3 coins of denomination 2 1 coin of denomination 5 1 coin of denomination 10 >>> test_coins = [coins[1][0], coins[1][1], coins[1][2], coins[2][0], coins[3][0]] >>> test_coins[0].denomination == test_coins[1].denomination == test_coins[2].denomination True >>> test_coins[0].denomination <Fraction: 2> >>> test_coins[3].denomination <Fraction: 5> >>> test_coins[4].denomination <Fraction: 10> >>> sumCurrencies(test_coins) <Fraction: 21> Now, this group of coins has some specific properties. There are only certain ways to get certain values of coins. We'll be testing 6, 11, 15, 16, 19, and 21 6 = 2 + 2 + 2 >>> wallet.coins = test_coins >>> sumCurrencies(wallet.coins) <Fraction: 21> >>> test(Fraction(6)) 6 11 = 5 + 2 + 2 + 2 >>> wallet.coins = test_coins >>> test(Fraction(11)) 11 15 = 10 + 5 >>> wallet.coins = test_coins >>> test(Fraction(15)) 15 16 = 10 + 2 + 2 + 2 >>> wallet.coins = test_coins >>> test(Fraction(16)) 16 19 = 10 + 5 + 2 + 2 >>> wallet.coins = test_coins >>> test(Fraction(19)) 19 21 = 10 + 5 + 2 + 2 + 2 >>> wallet.coins = [coins[1][0], coins[1][1], coins[1][2], coins[2][0], coins[3][0]] >>> test(Fraction(21)) 21 Okay. Some things we can't do. 8 = Impossible >>> wallet.coins = test_coins >>> test(Fraction(8)) Traceback (most recent call last): UnableToDoError: Not enough tokens 22 = Impossible >>> wallet.coins = test_coins >>> test(Fraction(22)) Traceback (most recent call last): UnableToDoError: Not enough tokens Okay. Now we want to make sure we don't lose coins if there is an exception that occurs. >>> test = lambda x: wallet.sendCoins('foo', '', x) >>> wallet.coins = test_coins >>> test(Fraction(21)) Traceback (most recent call last): AttributeError: 'str' object has no attribute ... >>> wallet.coins == test_coins True """ from containers import sumCurrencies from fraction import Fraction if not self.coins: raise UnableToDoError('Not enough tokens') if sumCurrencies(self.coins) < amount: raise UnableToDoError('Not enough tokens') denominations = {} # A dictionary of coins by denomination denomination_list = [] # A list of the denomination of every coin for coin in self.coins: denominations.setdefault(coin.denomination, []) denominations[coin.denomination].append(coin) denomination_list.append(coin.denomination) denomination_list.sort(reverse=True) # sort from high to low def my_split(piece_list, sum): # piece_list must be sorted from high to low # Delete all coins greater than sum my_list = [p for p in piece_list if p <= sum] while my_list: test_piece = my_list[0] del my_list[0] if test_piece == sum: return [test_piece] test_partition = my_split(my_list, sum - test_piece) if test_partition == [] : # Partitioning the rest failed, so remove all pieces of this size my_list = [p for p in my_list if p < test_piece] else : test_partition.append(test_piece) return test_partition # if we are here, we don't have a set of coins that works return [] if reduce(Fraction.__add__, denomination_list, Fraction(0)) != sumCurrencies(self.coins): raise Exception('denomination_list and self.coins differ!') denominations_to_use = my_split(denomination_list, amount) if not denominations_to_use: raise UnableToDoError('Not enough tokens') denominations_to_use = [str(d) for d in denominations_to_use] to_use = [] for denomination in denominations_to_use: to_use.append(denominations[denomination].pop()) # Make sure we remove the coins from denominations! for coin in to_use: # Remove the coins to prevent accidental double spending self.coins.remove(coin) try: protocol = protocols.TokenSpendSender(to_use,target) transport.setProtocol(protocol) transport.start() protocol.newMessage(Message(None)) except: # Catch everything. Losing coins is death. We re-raise anyways. # If we had an error at the protocol or transport layer, make sure we don't lose the coins self.coins.extend(to_use) raise if protocol.state != protocol.finish: # If we didn't succeed, re-add the coins to the wallet. # Of course, we may need to remint, so FIXME: look at this self.coins.extend(to_use)
def handleCoins(self, message): import base64 if message.type == 'TOKEN_SPEND': try: encoded_transaction_id, tokens, target = message.data except ValueError: return ProtocolErrorMessage('TS') if not isinstance(encoded_transaction_id, types.StringType): return ProtocolErrorMessage('TS') if not isinstance(tokens, types.ListType): return ProtocolErrorMessage('TS') if not tokens: # We require tokens return ProtocolErrorMessage('TS') for token in tokens: if not isinstance(token, types.ListType): return ProtocolErrorMessage('TS') # Convert transaction_id try: transaction_id = base64.b64decode(encoded_transaction_id) except TypeError: return ProtocolErrorMessage('TS') # Convert the tokens try: tokens = [ containers.CurrencyCoin().fromPython(c) for c in tokens ] except AttributeError: #FIXME: this is the wrong error, I'm pretty sure return ProtocolErrorMessage('TS') # And now do things from containers import sumCurrencies #be conservative result = Message('TOKEN_REJECT', 'default') if transaction_id != self.transaction_id: #FIXME: is the a PROTOCOL_ERROR? result = Message('TOKEN_REJECT', 'Rejected') elif sumCurrencies(tokens) != self.sum: result = Message('TOKEN_REJECT', 'Rejected') elif target != self.target: result = Message('TOKEN_REJECT', 'Rejected') elif self.action in ['redeem', 'exchange', 'trust']: out = self.wallet.handleIncomingCoins(tokens, self.action, target) if out: result = Message('TOKEN_ACCEPT') self.state = self.goodbye return result elif message.type == 'PROTOCOL_ERROR': pass else: return ProtocolErrorMessage('TokenSendRecipient')
if failures: self.issuer.dsdb.unlock(transaction_id) return Message('TRANSFER_TOKEN_REJECT', [ encoded_transaction_id, 'Blind', 'Invalid key_identifier', [] ]) # Make sure that we have the same amount of coins as mintings from fraction import Fraction from containers import sumCurrencies total = Fraction('0') for b in blinds: total += b[0].denomination * len(b[1]) if total != sumCurrencies(coins): self.issuer.dsdb.unlock(transaction_id) return Message( 'TRANSFER_TOKEN_REJECT', [encoded_transaction_id, 'Generic', 'Rejected', []]) # mint them immediately (the only thing we can do right now with the mint) minted = [] from entities import MintError for key, blindlist in blinds: this_set = [] for blind in blindlist: try: signature = self.issuer.mint.signNow( key.key_identifier, blind) except MintError:
def sendCoins(self, transport, target, amount=None): """sendCoins sends coins over a transport to a target of a certain amount. We need to be careful to try every possible working combination of coins to to an amount. To test, we muck in the internals of the coin sending to make sure that we get the right coins. (Note: This would be easier if we just had a function to get the coins for an amount.) To test the functionality we steal the transport, and when a message occurs, we steal the tokens directly out of the protocol. This is highly dependant on the internal details of TokenSpendSender and the transport/protocol relationship. >>> class transport: ... from containers import sumCurrencies ... def setProtocol(self, protocol): ... protocol.transport = self ... self.protocol = protocol ... def start(self): pass ... def write(self, info): print sumCurrencies(self.protocol.coins) # Steal the values >>> wallet = Wallet() >>> test = lambda x: wallet.sendCoins(transport(), '', x) >>> from tests import coins >>> from containers import sumCurrencies >>> from fraction import Fraction Okay. Do some simple checks to make sure things work at all >>> wallet.coins = [coins[0][0]] >>> test(Fraction(1)) 1 >>> wallet.coins = [coins[0][0], coins[2][0]] >>> test(Fraction(6)) 6 >>> wallet.coins = [coins[2][0], coins[0][0]] >>> test(Fraction(6)) 6 Okay. Now we'll do some more advanced tests of the system. We start off with a specifically selected group of coins: 3 coins of denomination 2 1 coin of denomination 5 1 coin of denomination 10 >>> test_coins = [coins[1][0], coins[1][1], coins[1][2], coins[2][0], coins[3][0]] >>> test_coins[0].denomination == test_coins[1].denomination == test_coins[2].denomination True >>> test_coins[0].denomination <Fraction: 2> >>> test_coins[3].denomination <Fraction: 5> >>> test_coins[4].denomination <Fraction: 10> >>> sumCurrencies(test_coins) <Fraction: 21> Now, this group of coins has some specific properties. There are only certain ways to get certain values of coins. We'll be testing 6, 11, 15, 16, 19, and 21 6 = 2 + 2 + 2 >>> wallet.coins = test_coins >>> sumCurrencies(wallet.coins) <Fraction: 21> >>> test(Fraction(6)) 6 11 = 5 + 2 + 2 + 2 >>> wallet.coins = test_coins >>> test(Fraction(11)) 11 15 = 10 + 5 >>> wallet.coins = test_coins >>> test(Fraction(15)) 15 16 = 10 + 2 + 2 + 2 >>> wallet.coins = test_coins >>> test(Fraction(16)) 16 19 = 10 + 5 + 2 + 2 >>> wallet.coins = test_coins >>> test(Fraction(19)) 19 21 = 10 + 5 + 2 + 2 + 2 >>> wallet.coins = [coins[1][0], coins[1][1], coins[1][2], coins[2][0], coins[3][0]] >>> test(Fraction(21)) 21 Okay. Some things we can't do. 8 = Impossible >>> wallet.coins = test_coins >>> test(Fraction(8)) Traceback (most recent call last): UnableToDoError: Not enough tokens 22 = Impossible >>> wallet.coins = test_coins >>> test(Fraction(22)) Traceback (most recent call last): UnableToDoError: Not enough tokens Okay. Now we want to make sure we don't lose coins if there is an exception that occurs. >>> test = lambda x: wallet.sendCoins('foo', '', x) >>> wallet.coins = test_coins >>> test(Fraction(21)) Traceback (most recent call last): AttributeError: 'str' object has no attribute ... >>> wallet.coins == test_coins True """ from containers import sumCurrencies from fraction import Fraction if not self.coins: raise UnableToDoError('Not enough tokens') if sumCurrencies(self.coins) < amount: raise UnableToDoError('Not enough tokens') denominations = {} # A dictionary of coins by denomination denomination_list = [] # A list of the denomination of every coin for coin in self.coins: denominations.setdefault(coin.denomination, []) denominations[coin.denomination].append(coin) denomination_list.append(coin.denomination) denomination_list.sort(reverse=True) # sort from high to low def my_split(piece_list, sum): # piece_list must be sorted from high to low # Delete all coins greater than sum my_list = [p for p in piece_list if p <= sum] while my_list: test_piece = my_list[0] del my_list[0] if test_piece == sum: return [test_piece] test_partition = my_split(my_list, sum - test_piece) if test_partition == []: # Partitioning the rest failed, so remove all pieces of this size my_list = [p for p in my_list if p < test_piece] else: test_partition.append(test_piece) return test_partition # if we are here, we don't have a set of coins that works return [] if reduce(Fraction.__add__, denomination_list, Fraction(0)) != sumCurrencies(self.coins): raise Exception('denomination_list and self.coins differ!') denominations_to_use = my_split(denomination_list, amount) if not denominations_to_use: raise UnableToDoError('Not enough tokens') denominations_to_use = [str(d) for d in denominations_to_use] to_use = [] for denomination in denominations_to_use: to_use.append(denominations[denomination].pop( )) # Make sure we remove the coins from denominations! for coin in to_use: # Remove the coins to prevent accidental double spending self.coins.remove(coin) try: protocol = protocols.TokenSpendSender(to_use, target) transport.setProtocol(protocol) transport.start() protocol.newMessage(Message(None)) except: # Catch everything. Losing coins is death. We re-raise anyways. # If we had an error at the protocol or transport layer, make sure we don't lose the coins self.coins.extend(to_use) raise if protocol.state != protocol.finish: # If we didn't succeed, re-add the coins to the wallet. # Of course, we may need to remint, so FIXME: look at this self.coins.extend(to_use)