def asset_conservation(db): logger.debug('Checking for conservation of assets.') supplies = util.supplies(db) for asset in supplies.keys(): issued = supplies[asset] held = sum([holder['address_quantity'] for holder in util.holders(db, asset)]) if held != issued: raise SanityError('{} {} issued ≠ {} {} held'.format(util.value_out(db, issued, asset), asset, util.value_out(db, held, asset), asset)) logger.debug('{} has been conserved ({} {} both issued and held)'.format(asset, util.value_out(db, issued, asset), asset))
def asset_conservation(db): logger.debug('Checking for conservation of assets.') supplies = util.supplies(db) for asset in supplies.keys(): issued = supplies[asset] held = sum( [holder['address_quantity'] for holder in util.holders(db, asset)]) if held != issued: raise SanityError('{} {} issued ≠ {} {} held'.format( util.value_out(db, issued, asset), asset, util.value_out(db, held, asset), asset)) logger.debug( '{} has been conserved ({} {} both issued and held)'.format( asset, util.value_out(db, issued, asset), asset))
print('Asset Name:', args.asset) print('Asset ID:', asset_id) print('Divisible:', divisible) print('Supply:', supply) print('Issuer:', results['issuer']) print('Callable:', results['callable']) print('Call Date:', call_date) print('Call Price:', call_price) print('Description:', '‘' + results['description'] + '’') if args.asset != 'BTC': print('Shareholders:') balances = util.api('get_balances', {'field': 'asset', 'op': '==', 'value': args.asset}) print('\taddress, quantity, escrow') for holder in util.holders(db, args.asset): quantity = holder['address_quantity'] if not quantity: continue quantity = util.devise(db, quantity, args.asset, 'output') if holder['escrow']: escrow = holder['escrow'] else: escrow = 'None' print('\t' + str(holder['address']) + ',' + str(quantity) + ',' + escrow) elif args.action == 'wallet': total_table = PrettyTable(['Asset', 'Balance']) totals = {} print() for bunch in bitcoin.get_wallet(): address, btc_balance = bunch[:2]
def validate (db, source, quantity_per_unit, asset, dividend_asset, block_index): cursor = db.cursor() problems = [] if asset == config.BTC: problems.append('cannot pay dividends to holders of {}'.format(config.BTC)) if asset == config.XCP: if (not block_index >= 317500) or block_index >= 320000 or config.TESTNET: # Protocol change. problems.append('cannot pay dividends to holders of {}'.format(config.XCP)) if quantity_per_unit <= 0: problems.append('non‐positive quantity per unit') # Examine asset. issuances = list(cursor.execute('''SELECT * FROM issuances WHERE (status = ? AND asset = ?) ORDER BY tx_index ASC''', ('valid', asset))) if not issuances: problems.append('no such asset, {}.'.format(asset)) return None, None, problems, 0 divisible = issuances[0]['divisible'] # Only issuer can pay dividends. if block_index >= 320000 or config.TESTNET: # Protocol change. if issuances[-1]['issuer'] != source: problems.append('only issuer can pay dividends') # Examine dividend asset. if dividend_asset in (config.BTC, config.XCP): dividend_divisible = True else: issuances = list(cursor.execute('''SELECT * FROM issuances WHERE (status = ? AND asset = ?)''', ('valid', dividend_asset))) if not issuances: problems.append('no such dividend asset, {}.'.format(dividend_asset)) return None, None, problems, 0 dividend_divisible = issuances[0]['divisible'] # Calculate dividend quantities. holders = util.holders(db, asset) outputs = [] addresses = [] dividend_total = 0 for holder in holders: if block_index < 294500 and not config.TESTNET: # Protocol change. if holder['escrow']: continue address = holder['address'] address_quantity = holder['address_quantity'] if block_index >= 296000 or config.TESTNET: # Protocol change. if address == source: continue dividend_quantity = address_quantity * quantity_per_unit if divisible: dividend_quantity /= config.UNIT if not dividend_divisible: dividend_quantity /= config.UNIT if dividend_asset == config.BTC and dividend_quantity < config.DEFAULT_MULTISIG_DUST_SIZE: continue # A bit hackish. dividend_quantity = int(dividend_quantity) outputs.append({'address': address, 'address_quantity': address_quantity, 'dividend_quantity': dividend_quantity}) addresses.append(address) dividend_total += dividend_quantity if not dividend_total: problems.append('zero dividend') if dividend_asset != config.BTC: balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, dividend_asset))) if not balances or balances[0]['quantity'] < dividend_total: problems.append('insufficient funds ({})'.format(dividend_asset)) fee = 0 if not problems and dividend_asset != config.BTC: holder_count = len(set(addresses)) if block_index >= 330000 or config.TESTNET: # Protocol change. fee = int(0.0002 * config.UNIT * holder_count) if fee: balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, config.XCP))) if not balances or balances[0]['quantity'] < fee: problems.append('insufficient funds ({})'.format(config.XCP)) cursor.close() return dividend_total, outputs, problems, fee
print('Asset Name:', args.asset) print('Asset ID:', asset_id) print('Divisible:', divisible) print('Supply:', supply) print('Issuer:', results['issuer']) print('Callable:', results['callable']) print('Call Date:', call_date) print('Call Price:', call_price) print('Description:', '‘' + results['description'] + '’') if args.asset != 'BTC': print('Shareholders:') balances = util.get_balances(db, asset=args.asset) print('\taddress, quantity, escrow') for holder in util.holders(db, args.asset): quantity = holder['address_quantity'] if not quantity: continue quantity = util.devise(db, quantity, args.asset, 'output') if holder['escrow']: escrow = holder['escrow'] else: escrow = 'None' print('\t' + str(holder['address']) + ',' + str(quantity) + ',' + escrow) elif args.action == 'wallet': total_table = PrettyTable(['Asset', 'Balance']) totals = {} print() for bunch in bitcoin.get_wallet(): address, btc_balance = bunch[:2]
def get_holder_count(asset): holders = util.holders(db, asset) addresses = [] for holder in holders: addresses.append(holder['address']) return {asset: len(set(addresses))}
def validate (db, source, fraction, asset, block_time, block_index, parse): cursor = db.cursor() problems = [] # TODO if not config.TESTNET: problems.append('callbacks are currently disabled on mainnet') return None, None, None, problems # TODO if fraction > 1: problems.append('fraction greater than one') elif fraction <= 0: problems.append('non‐positive fraction') issuances = list(cursor.execute('''SELECT * FROM issuances WHERE (status = ? AND asset = ?)''', ('valid', asset))) if not issuances: problems.append('no such asset, {}.'.format(asset)) return None, None, None, problems else: last_issuance = issuances[-1] if last_issuance['issuer'] != source: problems.append('not asset owner') return None, None, None, problems if not last_issuance['callable']: problems.append('uncallable asset') return None, None, None, problems elif last_issuance['call_date'] > block_time: problems.append('before call date') call_price = round(last_issuance['call_price'], 6) # TODO: arbitrary divisible = last_issuance['divisible'] if not divisible: # Pay per output unit. call_price *= config.UNIT # If parsing, unescrow all funds of asset. (Order of operations is # important here.) if parse: # Cancel pending order matches involving asset. cursor.execute('''SELECT * from order_matches \ WHERE status = ? AND (forward_asset = ? OR backward_asset = ?)''', ('pending', asset, asset)) for order_match in list(cursor): order.cancel_order_match(db, order_match, 'cancelled', block_index) # Cancel open orders involving asset. cursor.execute('''SELECT * from orders \ WHERE status = ? AND (give_asset = ? OR get_asset = ?)''', ('open', asset, asset)) for order_element in list(cursor): order.cancel_order(db, order_element, 'cancelled', block_index) # Calculate callback quantities. holders = util.holders(db, asset) outputs = [] for holder in holders: # If composing (and not parsing), predict funds to be returned from # escrow (instead of cancelling open offers, etc.), by *not* skipping # listing escrowed funds here. if parse and holder['escrow']: continue address = holder['address'] address_quantity = holder['address_quantity'] if address == source or address_quantity == 0: continue callback_quantity = int(address_quantity * fraction) # Round down. fraction_actual = callback_quantity / address_quantity outputs.append({'address': address, 'address_quantity': address_quantity, 'callback_quantity': callback_quantity, 'fraction_actual': fraction_actual}) callback_total = sum([output['callback_quantity'] for output in outputs]) if not callback_total: problems.append('nothing called back') balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, config.XCP))) if not balances or balances[0]['quantity'] < (call_price * callback_total): problems.append('insufficient funds') cursor.close() return call_price, callback_total, outputs, problems