class CoinWallet(object): ''' Class for a wallet ''' def __init__(self, wallet_config): self._errors = [] self._config = {} self._cache = Cacher({ 'accounts': {}, 'transactions': {}, 'transactiondetails': {}, 'balances': {}, 'info': {}, }) if type(wallet_config) is dict: self._config = wallet_config @property def provider_id(self): ''' Property for the provider id ''' return self.get('id', None) @property def enabled(self): ''' Return the status (enabled/disabled) ''' return self.get('enabled', False) def __getitem__(self, key): ''' Getter for dictionary-line behavior ''' if key == "currency_symbol": return self.getCurrencySymbol() elif key == "currency_code": return self.getCurrencyCode() config = getattr(self, '_config') return config[key] def __setitem__(self, key, value): ''' Setter for dictionary-line behavior ''' config = getattr(self, '_config') config[key] = value return setattr(self, '_config', config) def get(self, key, default=False): ''' Implementing .get() method for dictionary-line behavior ''' if self._config.get(key, False): return self._config.get(key, False) else: return default def haskey(self, key): ''' Check the existence of key ''' if key in self._config.keys(): return True else: return False def getNet(self): ''' Return network value, mainnet or testnet ''' # check for cached data, use that or get it again cache_hash = self.getParamHash("provider_id=%s" % self.provider_id) cached_peerinfo = self._cache.fetch('info', cache_hash) if cached_peerinfo: info = cached_peerinfo else: info = connector.getInfo(self.provider_id) self._cache.store('info', cache_hash, info) is_testnet = False if info.has_key('testnet'): is_testnet = info.get('testnet') if is_testnet is False: return "mainnet" elif is_testnet is True: return "testnet" else: # default to mainnet return "mainnet" def balance(self): ''' Get wallet balance ''' # check for cached data, use that or get it again cache_hash = self.getParamHash("balance") cached_object = self._cache.fetch('balance', cache_hash) if cached_object: return cached_object.get(self.provider_id, "-") balance = connector.getBalance(self.provider_id) # store result in cache self._cache.store('balance', cache_hash, balance) return misc.longNumber(balance.get(self.provider_id, "-")) def getParamHash(self, param=""): ''' This function takes a string and calculates a sha224 hash out of it. It is used to hash the input parameters of functions/method in order to uniquely identify a cached result based only on the input parameters of the function/method call. ''' cache_hash = hashlib.sha224(param).hexdigest() return cache_hash def listAccounts(self, gethidden=False, getarchived=False): ''' Get a list of accounts. This method also supports filtering, fetches address for each account etc. ''' # check for cached data, use that or get it again cache_hash = self.getParamHash("gethidden=%s&getarchived=%s" % (gethidden, getarchived)) cached_object = self._cache.fetch('accounts', cache_hash) if cached_object: return cached_object # get data from the connector (xxxcoind) fresh_accounts = connector.listAccounts( gethidden=False, getarchived=False, selected_provider_id=self.provider_id) # get a list of archived address address_ignore_list = [] if not getarchived: ignore_list = accountFilter.objects.filter(status=1) for ignored_account in ignore_list: address_ignore_list.append( ignored_account.address.encode('ascii')) # get a list of hidden accounts address_hidden_list = [] if not gethidden: hidden_list = accountFilter.objects.filter(status=2) for hidden_account in hidden_list: address_hidden_list.append( hidden_account.address.encode('ascii')) accountObjects = [] for account_name, account_balance in fresh_accounts.get( self.provider_id, {}).items(): ''' # check all addresses if they are in the archive list for ignored_address in address_ignore_list: if ignored_address in account_addresses: del account_addresses[account_addresses.index(ignored_address)] # check all addresses if they are in the hidden list hidden_flag = False for hidden_address in address_hidden_list: if hidden_address in account_addresses: hidden_flag = True ''' accountObjects.append( CoinAccount({ 'name': account_name, 'balance': account_balance, 'currency': self['currency'], 'provider_id': self.provider_id, 'wallet': self, })) # cache the result self._cache.store('accounts', cache_hash, accountObjects) return accountObjects def getCurrencySymbol(self): ''' Return the Unicode currency symbol ''' return self.get('symbol', None) def getCurrencyCode(self): ''' Return the currency code ''' return self.get('currency', "").lower() def listTransactions(self, limit=10, start=0): ''' Return a list of transactions wallet-wide ''' # check for cached data, use that or get it again cache_hash = self.getParamHash("limit=%s&start=%s" % (limit, start)) cached_object = self._cache.fetch('transactions', cache_hash) if cached_object: return cached_object transactions = [] transactions_dicts = connector.listTransactionsByAccount( "*", self.provider_id, limit, start) for transaction in transactions_dicts: transaction['wallet'] = self transaction['currency'] = self.getCurrencyCode() transaction['currency_symbol'] = self.getCurrencySymbol() transaction['provider_id'] = self.provider_id transactions.append(CoinTransaction(transaction)) self._cache.store('transactions', cache_hash, transactions) return transactions def getAccountByName(self, name): ''' Return CoinAccount() for name ''' accounts = self.listAccounts(gethidden=True, getarchived=True) for account in accounts: if account['name'] == name: return account return None def getTransactionById(self, txid): ''' Return a transaction by txid ''' # check for cached data, use that or get it again cache_hash = self.getParamHash("txid=%s" % (txid)) cached_object = self._cache.fetch('transactiondetails', cache_hash) if cached_object: return cached_object transaction_details = connector.getTransaction(txid, self.provider_id) transaction_details['currency'] = self.getCurrencyCode() transaction_details['wallet'] = self self._cache.store('transactiondetails', cache_hash, transaction_details) return CoinTransaction(transaction_details) def getAddressesByAccount(self, account): ''' Get a list of address for account ''' addresses_list = connector.getAddressesByAccount( account, self.provider_id) coinaddresses = [] for address in addresses_list: coinaddresses.append(CoinAddress(address, account)) return coinaddresses def getDefaultAccount(self): ''' Return the CoinAccount object for the default wallet account ''' accounts = self.listAccounts(gethidden=True, getarchived=True) for account in accounts: if len(account['name']) == 0: return account else: return None def getAccountByAddress(self, address): ''' Return account by address ''' accounts = self.listAccounts(gethidden=True, getarchived=True) target_account = None for account in accounts: for account_address in account['addresses']: if str(address) == str(account_address): target_account = account target_account['currency'] = self.getCurrencyCode() target_account['provider_id'] = self.provider_id return target_account else: return None def getAccountByIdentifier(self, identifier): ''' Get account by identifier ''' list_of_accounts = self.listAccounts(gethidden=True, getarchived=True) for account in list_of_accounts: if account.getIdentifier() == identifier: return account else: return None
class CoinAccount(object): ''' Class for an account ''' def __init__(self, accountDetails): self._errors = [] self._account = {} self._hidden = False self._cache = Cacher({ 'transactions': {}, 'balances': {}, 'addressesbyaccount': {}, }) if type(accountDetails) is dict: self._account = accountDetails self._provider_id = accountDetails['provider_id'] @property def provider_id(self): ''' Property for the provider id ''' return self.get('provider_id', None) def __getitem__(self, key): ''' Getter for dictionary-line behavior ''' if key == "addresses": return self.getAddresses() elif key == "last_activity": return self.getLastActivity() elif key == "currency_symbol": return self.getCurrencySymbol() elif key == "currency_code": return self.getCurrencyCode() elif key == 'identifier': return self.getIdentifier() account = getattr(self, '_account') return account.get(key, None) def __setitem__(self, key, value): ''' Setter for dictionary-line behavior ''' account = getattr(self, '_account') account[key] = value return setattr(self, '_account', account) def get(self, key, default=False): ''' Getter for dictionary-line behavior ''' if self._account.get(key, False): return self._account.get(key, False) else: return default def haskey(self, key): ''' Check the existence of key ''' if key in self._account.keys(): return True else: return False def getParamHash(self, param=""): ''' This function takes a string and calculates a sha224 hash out of it. It is used to hash the input parameters of functions/method in order to uniquely identify a cached result based only on the input parameters of the function/method call. ''' cache_hash = hashlib.sha224(param).hexdigest() return cache_hash def getIdentifier(self): ''' There is no unique identifier for an account in a xxxcoind daemon so lets make one. Hopefully the below hashing method will uniquely identify an account for us ''' unique_string = "provider_id=%s&name=%s¤cy=%s" % ( self['provider_id'], self['name'], self['currency']) identifier = hashlib.sha1(unique_string).hexdigest() return identifier def isDefault(self): ''' Return bool whether this is a default account or not ''' if self._account['name'] == u"": self._hidden = True return True else: return False def getBalance(self): ''' Return the account balance ''' balance = connector.getBalance(self.provider_id, self['name']) return misc.longNumber(balance) def isHidden(self): ''' Return bool if this account is hidden ''' return self._hidden or self._account['hidden'] or self.isDefault() def getAddresses(self): ''' Get the address for an account name ''' # check for cached data, use that or get it again cache_hash = self.getParamHash("name=%s" % (self['name'])) cached_object = self._cache.fetch('addressesbyaccount', cache_hash) if cached_object: return cached_object addresses = connector.getAddressesByAccount(self['name'], self.provider_id) addresses_list = [] for address in addresses: coinaddr = CoinAddress(address, self) addresses_list.append(coinaddr) # cache the result self._cache.store('addressesbyaccount', cache_hash, addresses_list) return addresses_list def getAddressesCount(self): ''' Return the number of address under this account ''' addresses = self.getAddresses() return len(addresses) def getLastActivity(self): ''' Return the date of the last activity ''' last_transaction = self.listTransactions(1, 0) if last_transaction: last_activity = misc.twitterizeDate(last_transaction[0]['time']) else: last_activity = "never" self['last_activity'] = last_activity return last_activity def getCurrencySymbol(self): ''' Return the Unicode currency symbol ''' return misc.getCurrencySymbol(connector, self.getCurrencyCode()) def getCurrencyCode(self): ''' Return the currency code ''' return self.get('currency', "").lower() def listTransactions(self, limit=100000, start=0, orderby='time', reverse=True): ''' Get a list of transactions by account name and provider_id ''' cache_hash = self.getParamHash( "limit=%s&start=%sorderby=%s&reverse=%s" % (limit, start, orderby, reverse)) cached_object = self._cache.fetch('transactions', cache_hash) if cached_object: return cached_object transactions = [] transaction_list = connector.listTransactionsByAccount( self['name'], self['provider_id'], limit, start) for entry in transaction_list: if entry.get('address', False): entry['address'] = CoinAddress(entry['address'], self) # give out a provider id and a currency code to the transaction dict entry['provider_id'] = self.provider_id entry['currency'] = self['currency'] if entry['category'] == 'receive': entry['source_address'] = CoinAddress( entry.get('details', {}).get('sender_address', False), "This is a sender address!") elif entry['category'] == 'send': entry['source_addresses'] = self[ 'wallet'].getAddressesByAccount(entry['account']) entry['wallet'] = self['wallet'] coin_transaction = CoinTransaction(entry) transactions.append(coin_transaction) # sort result transactions = sorted(transactions, key=lambda transaction: transaction[orderby], reverse=reverse) # cache the result self._cache.store('transactions', cache_hash, transactions) return transactions
class CoinTransaction(object): ''' Class for a transaction ''' # source: https://github.com/zamgo/PHPCoinAddress/blob/master/README.md prefixes = { 'btc': {'mainnet': '\x00', 'testnet': '\x6f'}, 'ltc': {'mainnet': '\x30', 'testnet': '\x6f'}, 'ftc': {'mainnet': '\x0E', 'testnet': '\x6f'}, 'ppc': {'mainnet': '\x37', 'testnet': '\x6f'}, 'nmc': {'mainnet': '\x34', 'testnet': '\x6f'}, 'nvc': {'mainnet': '\x08', 'testnet': '\x6f'}, 'doge': {'mainnet': '\x30', 'testnet': '\x6f'}, } def __init__(self, transactionDetails): self._transaction = {} self. _cache = Cacher({ 'details': {}, }) if type(transactionDetails) is dict: self._transaction = transactionDetails self['timereceived_pretty'] = misc.twitterizeDate(self.get('timereceived', 'never')) self['time_pretty'] = misc.twitterizeDate(self.get('time', 'never')) self['timereceived_human'] = datetime.datetime.fromtimestamp(self.get('timereceived', 0)) self['time_human'] = datetime.datetime.fromtimestamp(self.get('time', 0)) self['blocktime_human'] = datetime.datetime.fromtimestamp(self.get('blocktime', 0)) self['blocktime_pretty'] = misc.twitterizeDate(self.get('blocktime', 'never')) self['currency_symbol'] = misc.getCurrencySymbol(connector, self['currency']) if self.get('category', False) in ['receive', 'send']: if self['confirmations'] <= MainConfig['globals']['confirmation_limit']: self['status_icon'] = 'glyphicon-time' self['status_color'] = '#AAA'; self['tooltip'] = self['confirmations'] else: self['status_icon'] = 'glyphicon-ok-circle' self['status_color'] = '#1C9E3F'; self['tooltip'] = self['confirmations'] accountObject = self['wallet'].getAccountByName(self['account']) self['account'] = accountObject if self['category'] == 'receive': self['icon'] = 'glyphicon-circle-arrow-down' elif self['category'] == 'send': self['icon'] = 'glyphicon-circle-arrow-up' elif self['category'] == 'move': self['icon'] = 'glyphicon-circle-arrow-right' self['otheraccount'] = self['wallet'].getAccountByName(self['otheraccount']) def __getitem__(self, key): ''' Getter for dictionary-line behavior ''' if key == "account": if self.haskey("account"): return self._transaction['account'] else: return self._transaction['details'][0]['account'] elif key == "category": if self.haskey("category"): return self._transaction['category'] else: return self._transaction['details'][0]['category'] elif key == "currency_symbol": return self.getCurrencySymbol() elif key == "currency_code": return self.getCurrencyCode() elif key == "raw_transaction": return self.getRawTransaction() elif key == "source_address": return self.getSenderAddress() elif key == "address": return CoinAddress(self._transaction['address'], self._transaction['account']) transaction = getattr(self, '_transaction') return transaction.get(key, None) def __setitem__(self, key, value): ''' Setter for dictionary-line behavior ''' transaction = getattr(self, '_transaction') transaction[key] = value return setattr(self, '_transaction', transaction) def get(self, key, default=False): ''' get() method for dictionary-line behavior ''' if self._transaction.get(key, False): return self._transaction.get(key, False) else: return default def haskey(self, key): ''' Check the existence of key ''' if key in self._transaction.keys(): return True else: return False @property def provider_id(self): ''' Property for the provider id ''' return self.get('provider_id', None) @property def transaction_id(self): ''' Property for the txid ''' return self.get('txid', None) @property def txid(self): ''' Property for the txid ''' return self.get('txid', None) def getParamHash(self, param=""): ''' This function takes a string and calculates a sha224 hash out of it. It is used to hash the input parameters of functions/method in order to uniquely identify a cached result based only on the input parameters of the function/method call. ''' cache_hash = hashlib.sha224(param).hexdigest() return cache_hash def metaProperties(self): ''' Return transaction details, like sender address ''' raw_transaction = self.getRawTransaction() if self['category'] == 'receive': sender_address = self.decodeScriptSig(raw_transaction, self['currency'], self['wallet'].getNet()) else: sender_address = None return {'sender_address': sender_address} def getRawTransaction(self): ''' Get the raw transaction dict ''' cache_hash = self.getParamHash("details") cached_object = self._cache.fetch('details', cache_hash) if cached_object: raw_transaction = cached_object else: raw_transaction = connector.getRawTransaction(self['txid'], self['wallet']['provider_id']) self._cache.store('details', cache_hash, raw_transaction) return raw_transaction def getSenderAddress(self): ''' Getter function for the sender address ''' if self['category'] == 'receive': meta_properties = self.metaProperties() if meta_properties.get('sender_address', False): return CoinAddress(meta_properties['sender_address'], 'This is a sender address!') return None def decodeScriptSig(self, rawtransaction, currency, net='testnet'): ''' Decode input script signature, courtesy of: http://bitcoin.stackexchange.com/questions/7838/why-does-gettransaction-report-me-only-the-receiving-address/8864#8864 ''' try: script_sig = rawtransaction['vin'][0]['scriptSig']['asm'] except: return "not enough info" script = script_sig.split() h = hashlib.sha256(script[1].decode("hex")).digest() ripe160 = hashlib.new('ripemd160') ripe160.update(h) d = ripe160.digest() prefix = self.prefixes[currency.lower()][net] address = (prefix + d) # calculate checksum checksum = hashlib.sha256(hashlib.sha256(address).digest()).digest()[:4] # build the raw address address += checksum # encode the address in base58 encoded_address = misc.b58encode(address) return encoded_address def getCurrencySymbol(self): ''' Return the Unicode currency symbol ''' return misc.getCurrencySymbol(connector, self.getCurrencyCode()) def getCurrencyCode(self): ''' Return the currency code ''' return self.get('currency', "").lower()
class CoinAccount(object): ''' Class for an account ''' def __init__(self, accountDetails): self._errors = [] self._account = {} self._hidden = False self._cache = Cacher({ 'transactions': {}, 'balances': {}, 'addressesbyaccount': {}, }) if type(accountDetails) is dict: self._account = accountDetails self._provider_id = accountDetails['provider_id'] @property def provider_id(self): ''' Property for the provider id ''' return self.get('provider_id', None) def __getitem__(self, key): ''' Getter for dictionary-line behavior ''' if key == "addresses": return self.getAddresses() elif key == "last_activity": return self.getLastActivity() elif key == "currency_symbol": return self.getCurrencySymbol() elif key == "currency_code": return self.getCurrencyCode() elif key == 'identifier': return self.getIdentifier() account = getattr(self, '_account') return account.get(key, None) def __setitem__(self, key, value): ''' Setter for dictionary-line behavior ''' account = getattr(self, '_account') account[key] = value return setattr(self, '_account', account) def get(self, key, default=False): ''' Getter for dictionary-line behavior ''' if self._account.get(key, False): return self._account.get(key, False) else: return default def haskey(self, key): ''' Check the existence of key ''' if key in self._account.keys(): return True else: return False def getParamHash(self, param=""): ''' This function takes a string and calculates a sha224 hash out of it. It is used to hash the input parameters of functions/method in order to uniquely identify a cached result based only on the input parameters of the function/method call. ''' cache_hash = hashlib.sha224(param).hexdigest() return cache_hash def getIdentifier(self): ''' There is no unique identifier for an account in a xxxcoind daemon so lets make one. Hopefully the below hashing method will uniquely identify an account for us ''' unique_string = "provider_id=%s&name=%s¤cy=%s" % (self['provider_id'], self['name'], self['currency']) identifier = hashlib.sha1(unique_string).hexdigest() return identifier def isDefault(self): ''' Return bool whether this is a default account or not ''' if self._account['name'] == u"": self._hidden = True return True else: return False def getBalance(self): ''' Return the account balance ''' balance = connector.getBalance(self.provider_id, self['name']) return misc.longNumber(balance) def isHidden(self): ''' Return bool if this account is hidden ''' return self._hidden or self._account['hidden'] or self.isDefault() def getAddresses(self): ''' Get the address for an account name ''' # check for cached data, use that or get it again cache_hash = self.getParamHash("name=%s" % (self['name'])) cached_object = self._cache.fetch('addressesbyaccount', cache_hash) if cached_object: return cached_object addresses = connector.getAddressesByAccount(self['name'], self.provider_id) addresses_list = [] for address in addresses: coinaddr = CoinAddress(address, self) addresses_list.append(coinaddr) # cache the result self._cache.store('addressesbyaccount', cache_hash, addresses_list) return addresses_list def getAddressesCount(self): ''' Return the number of address under this account ''' addresses = self.getAddresses() return len(addresses) def getLastActivity(self): ''' Return the date of the last activity ''' last_transaction = self.listTransactions(1, 0) if last_transaction: last_activity = misc.twitterizeDate(last_transaction[0]['time']) else: last_activity = "never" self['last_activity'] = last_activity return last_activity def getCurrencySymbol(self): ''' Return the Unicode currency symbol ''' return misc.getCurrencySymbol(connector, self.getCurrencyCode()) def getCurrencyCode(self): ''' Return the currency code ''' return self.get('currency', "").lower() def listTransactions(self, limit=100000, start=0, orderby='time', reverse=True): ''' Get a list of transactions by account name and provider_id ''' cache_hash = self.getParamHash("limit=%s&start=%sorderby=%s&reverse=%s" % (limit, start, orderby, reverse)) cached_object = self._cache.fetch('transactions', cache_hash) if cached_object: return cached_object transactions = [] transaction_list = connector.listTransactionsByAccount(self['name'], self['provider_id'], limit, start) for entry in transaction_list: if entry.get('address', False): entry['address'] = CoinAddress(entry['address'], self) # give out a provider id and a currency code to the transaction dict entry['provider_id'] = self.provider_id entry['currency'] = self['currency'] if entry['category'] == 'receive': entry['source_address'] = CoinAddress(entry.get('details', {}).get('sender_address', False), "This is a sender address!") elif entry['category'] == 'send': entry['source_addresses'] = self['wallet'].getAddressesByAccount(entry['account']) entry['wallet'] = self['wallet'] coin_transaction = CoinTransaction(entry) transactions.append(coin_transaction) # sort result transactions = sorted(transactions, key=lambda transaction: transaction[orderby], reverse=reverse) # cache the result self._cache.store('transactions', cache_hash, transactions) return transactions
class CoinTransaction(object): ''' Class for a transaction ''' # source: https://github.com/zamgo/PHPCoinAddress/blob/master/README.md prefixes = { 'btc': { 'mainnet': '\x00', 'testnet': '\x6f' }, 'ltc': { 'mainnet': '\x30', 'testnet': '\x6f' }, 'ftc': { 'mainnet': '\x0E', 'testnet': '\x6f' }, 'ppc': { 'mainnet': '\x37', 'testnet': '\x6f' }, 'nmc': { 'mainnet': '\x34', 'testnet': '\x6f' }, 'nvc': { 'mainnet': '\x08', 'testnet': '\x6f' }, 'doge': { 'mainnet': '\x30', 'testnet': '\x6f' }, } def __init__(self, transactionDetails): self._transaction = {} self._cache = Cacher({ 'details': {}, }) if type(transactionDetails) is dict: self._transaction = transactionDetails self['timereceived_pretty'] = misc.twitterizeDate( self.get('timereceived', 'never')) self['time_pretty'] = misc.twitterizeDate(self.get( 'time', 'never')) self['timereceived_human'] = datetime.datetime.fromtimestamp( self.get('timereceived', 0)) self['time_human'] = datetime.datetime.fromtimestamp( self.get('time', 0)) self['blocktime_human'] = datetime.datetime.fromtimestamp( self.get('blocktime', 0)) self['blocktime_pretty'] = misc.twitterizeDate( self.get('blocktime', 'never')) self['currency_symbol'] = misc.getCurrencySymbol( connector, self['currency']) if self.get('category', False) in ['receive', 'send']: if self['confirmations'] <= MainConfig['globals'][ 'confirmation_limit']: self['status_icon'] = 'glyphicon-time' self['status_color'] = '#AAA' self['tooltip'] = self['confirmations'] else: self['status_icon'] = 'glyphicon-ok-circle' self['status_color'] = '#1C9E3F' self['tooltip'] = self['confirmations'] accountObject = self['wallet'].getAccountByName(self['account']) self['account'] = accountObject if self['category'] == 'receive': self['icon'] = 'glyphicon-circle-arrow-down' elif self['category'] == 'send': self['icon'] = 'glyphicon-circle-arrow-up' elif self['category'] == 'move': self['icon'] = 'glyphicon-circle-arrow-right' self['otheraccount'] = self['wallet'].getAccountByName( self['otheraccount']) def __getitem__(self, key): ''' Getter for dictionary-line behavior ''' if key == "account": if self.haskey("account"): return self._transaction['account'] else: return self._transaction['details'][0]['account'] elif key == "category": if self.haskey("category"): return self._transaction['category'] else: return self._transaction['details'][0]['category'] elif key == "currency_symbol": return self.getCurrencySymbol() elif key == "currency_code": return self.getCurrencyCode() elif key == "raw_transaction": return self.getRawTransaction() elif key == "source_address": return self.getSenderAddress() elif key == "address": return CoinAddress(self._transaction['address'], self._transaction['account']) transaction = getattr(self, '_transaction') return transaction.get(key, None) def __setitem__(self, key, value): ''' Setter for dictionary-line behavior ''' transaction = getattr(self, '_transaction') transaction[key] = value return setattr(self, '_transaction', transaction) def get(self, key, default=False): ''' get() method for dictionary-line behavior ''' if self._transaction.get(key, False): return self._transaction.get(key, False) else: return default def haskey(self, key): ''' Check the existence of key ''' if key in self._transaction.keys(): return True else: return False @property def provider_id(self): ''' Property for the provider id ''' return self.get('provider_id', None) @property def transaction_id(self): ''' Property for the txid ''' return self.get('txid', None) @property def txid(self): ''' Property for the txid ''' return self.get('txid', None) def getParamHash(self, param=""): ''' This function takes a string and calculates a sha224 hash out of it. It is used to hash the input parameters of functions/method in order to uniquely identify a cached result based only on the input parameters of the function/method call. ''' cache_hash = hashlib.sha224(param).hexdigest() return cache_hash def metaProperties(self): ''' Return transaction details, like sender address ''' raw_transaction = self.getRawTransaction() if self['category'] == 'receive': sender_address = self.decodeScriptSig(raw_transaction, self['currency'], self['wallet'].getNet()) else: sender_address = None return {'sender_address': sender_address} def getRawTransaction(self): ''' Get the raw transaction dict ''' cache_hash = self.getParamHash("details") cached_object = self._cache.fetch('details', cache_hash) if cached_object: raw_transaction = cached_object else: raw_transaction = connector.getRawTransaction( self['txid'], self['wallet']['provider_id']) self._cache.store('details', cache_hash, raw_transaction) return raw_transaction def getSenderAddress(self): ''' Getter function for the sender address ''' if self['category'] == 'receive': meta_properties = self.metaProperties() if meta_properties.get('sender_address', False): return CoinAddress(meta_properties['sender_address'], 'This is a sender address!') return None def decodeScriptSig(self, rawtransaction, currency, net='testnet'): ''' Decode input script signature, courtesy of: http://bitcoin.stackexchange.com/questions/7838/why-does-gettransaction-report-me-only-the-receiving-address/8864#8864 ''' try: script_sig = rawtransaction['vin'][0]['scriptSig']['asm'] except: return "not enough info" script = script_sig.split() h = hashlib.sha256(script[1].decode("hex")).digest() ripe160 = hashlib.new('ripemd160') ripe160.update(h) d = ripe160.digest() prefix = self.prefixes[currency.lower()][net] address = (prefix + d) # calculate checksum checksum = hashlib.sha256( hashlib.sha256(address).digest()).digest()[:4] # build the raw address address += checksum # encode the address in base58 encoded_address = misc.b58encode(address) return encoded_address def getCurrencySymbol(self): ''' Return the Unicode currency symbol ''' return misc.getCurrencySymbol(connector, self.getCurrencyCode()) def getCurrencyCode(self): ''' Return the currency code ''' return self.get('currency', "").lower()
class CoinWallet(object): ''' Class for a wallet ''' def __init__(self, wallet_config): self._errors = [] self._config = {} self._cache = Cacher({ 'accounts': {}, 'transactions': {}, 'transactiondetails': {}, 'balances': {}, 'info': {}, }) if type(wallet_config) is dict: self._config = wallet_config @property def provider_id(self): ''' Property for the provider id ''' return self.get('id', None) @property def enabled(self): ''' Return the status (enabled/disabled) ''' return self.get('enabled', False) def __getitem__(self, key): ''' Getter for dictionary-line behavior ''' if key == "currency_symbol": return self.getCurrencySymbol() elif key == "currency_code": return self.getCurrencyCode() config = getattr(self, '_config') return config[key] def __setitem__(self, key, value): ''' Setter for dictionary-line behavior ''' config = getattr(self, '_config') config[key] = value return setattr(self, '_config', config) def get(self, key, default=False): ''' Implementing .get() method for dictionary-line behavior ''' if self._config.get(key, False): return self._config.get(key, False) else: return default def haskey(self, key): ''' Check the existence of key ''' if key in self._config.keys(): return True else: return False def getNet(self): ''' Return network value, mainnet or testnet ''' # check for cached data, use that or get it again cache_hash = self.getParamHash("provider_id=%s" % self.provider_id) cached_peerinfo = self._cache.fetch('info', cache_hash) if cached_peerinfo: info = cached_peerinfo else: info = connector.getInfo(self.provider_id) self._cache.store('info', cache_hash, info) is_testnet = False if info.has_key('testnet'): is_testnet = info.get('testnet') if is_testnet is False: return "mainnet" elif is_testnet is True: return "testnet" else: # default to mainnet return "mainnet" def balance(self): ''' Get wallet balance ''' # check for cached data, use that or get it again cache_hash = self.getParamHash("balance") cached_object = self._cache.fetch('balance', cache_hash) if cached_object: return cached_object.get(self.provider_id, "-") balance = connector.getBalance(self.provider_id) # store result in cache self._cache.store('balance', cache_hash, balance) return misc.longNumber(balance.get(self.provider_id, "-")) def getParamHash(self, param=""): ''' This function takes a string and calculates a sha224 hash out of it. It is used to hash the input parameters of functions/method in order to uniquely identify a cached result based only on the input parameters of the function/method call. ''' cache_hash = hashlib.sha224(param).hexdigest() return cache_hash def listAccounts(self, gethidden=False, getarchived=False): ''' Get a list of accounts. This method also supports filtering, fetches address for each account etc. ''' # check for cached data, use that or get it again cache_hash = self.getParamHash("gethidden=%s&getarchived=%s" % (gethidden, getarchived)) cached_object = self._cache.fetch('accounts', cache_hash) if cached_object: return cached_object # get data from the connector (xxxcoind) fresh_accounts = connector.listAccounts(gethidden=False, getarchived=False, selected_provider_id=self.provider_id) # get a list of archived address address_ignore_list = [] if not getarchived: ignore_list = accountFilter.objects.filter(status=1) for ignored_account in ignore_list: address_ignore_list.append(ignored_account.address.encode('ascii')) # get a list of hidden accounts address_hidden_list = [] if not gethidden: hidden_list = accountFilter.objects.filter(status=2) for hidden_account in hidden_list: address_hidden_list.append(hidden_account.address.encode('ascii')) accountObjects = [] for account_name, account_balance in fresh_accounts.get(self.provider_id, {}).items(): ''' # check all addresses if they are in the archive list for ignored_address in address_ignore_list: if ignored_address in account_addresses: del account_addresses[account_addresses.index(ignored_address)] # check all addresses if they are in the hidden list hidden_flag = False for hidden_address in address_hidden_list: if hidden_address in account_addresses: hidden_flag = True ''' accountObjects.append(CoinAccount({ 'name': account_name, 'balance': account_balance, 'currency': self['currency'], 'provider_id': self.provider_id, 'wallet': self, })) # cache the result self._cache.store('accounts', cache_hash, accountObjects) return accountObjects def getCurrencySymbol(self): ''' Return the Unicode currency symbol ''' return self.get('symbol', None) def getCurrencyCode(self): ''' Return the currency code ''' return self.get('currency', "").lower() def listTransactions(self, limit=10, start=0): ''' Return a list of transactions wallet-wide ''' # check for cached data, use that or get it again cache_hash = self.getParamHash("limit=%s&start=%s" % (limit, start)) cached_object = self._cache.fetch('transactions', cache_hash) if cached_object: return cached_object transactions = [] transactions_dicts = connector.listTransactionsByAccount("*", self.provider_id, limit, start) for transaction in transactions_dicts: transaction['wallet'] = self transaction['currency'] = self.getCurrencyCode() transaction['currency_symbol'] = self.getCurrencySymbol() transaction['provider_id'] = self.provider_id transactions.append(CoinTransaction(transaction)) self._cache.store('transactions', cache_hash, transactions) return transactions def getAccountByName(self, name): ''' Return CoinAccount() for name ''' accounts = self.listAccounts(gethidden=True, getarchived=True) for account in accounts: if account['name'] == name: return account return None def getTransactionById(self, txid): ''' Return a transaction by txid ''' # check for cached data, use that or get it again cache_hash = self.getParamHash("txid=%s" % (txid)) cached_object = self._cache.fetch('transactiondetails', cache_hash) if cached_object: return cached_object transaction_details = connector.getTransaction(txid, self.provider_id) transaction_details['currency'] = self.getCurrencyCode() transaction_details['wallet'] = self self._cache.store('transactiondetails', cache_hash, transaction_details) return CoinTransaction(transaction_details) def getAddressesByAccount(self, account): ''' Get a list of address for account ''' addresses_list = connector.getAddressesByAccount(account, self.provider_id) coinaddresses = [] for address in addresses_list: coinaddresses.append(CoinAddress(address, account)) return coinaddresses def getDefaultAccount(self): ''' Return the CoinAccount object for the default wallet account ''' accounts = self.listAccounts(gethidden=True, getarchived=True) for account in accounts: if len(account['name']) == 0: return account else: return None def getAccountByAddress(self, address): ''' Return account by address ''' accounts = self.listAccounts(gethidden=True, getarchived=True) target_account = None for account in accounts: for account_address in account['addresses']: if str(address) == str(account_address): target_account = account target_account['currency'] = self.getCurrencyCode() target_account['provider_id'] = self.provider_id return target_account else: return None def getAccountByIdentifier(self, identifier): ''' Get account by identifier ''' list_of_accounts = self.listAccounts(gethidden=True, getarchived=True) for account in list_of_accounts: if account.getIdentifier() == identifier: return account else: return None