Example #1
0
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))
Example #2
0
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]
Example #4
0
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
Example #5
0
        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]
Example #6
0
 def get_holder_count(asset):
     holders = util.holders(db, asset)
     addresses = []
     for holder in holders:
         addresses.append(holder['address'])
     return {asset: len(set(addresses))}
Example #7
0
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
Example #8
0
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
Example #9
0
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
Example #10
0
 def get_holder_count(asset):
     holders = util.holders(db, asset)
     addresses = []
     for holder in holders:
         addresses.append(holder['address'])
     return {asset: len(set(addresses))}