Example #1
0
def validate (db, source, asset_, give_quantity, escrow_quantity, mainchainrate, status, block_index):
    problems = []
    asset_id = None

    if not util.enabled('dispensers'):
        problems.append('not activated yet.')

    if asset_ == config.BTC:
        problems.append('cannot dispense %s' % config.BTC)
        return None, problems

    # resolve subassets
    asset = util.resolve_subasset_longname(db, asset_)

    if status == STATUS_OPEN:
        if not issuance.is_vendable(db, asset):
            problems.append('asset "%s" is not vendable' % asset_)
        if give_quantity <= 0: problems.append('give_quantity must be positive')
        if mainchainrate <= 0: problems.append('mainchainrate must be positive')
        if escrow_quantity < give_quantity:
            problems.append('escrow_quantity must be greater or equal than give_quantity')
    elif not(status == STATUS_CLOSED):
        problems.append('invalid status %i' % status)

    cursor = db.cursor()
    cursor.execute('''SELECT quantity FROM balances \
                      WHERE address = ? and asset = ?''', (source,asset,))
    available = cursor.fetchall()

    if len(available) == 0:
        problems.append('address doesn\'t has the asset %s' % asset_)
    elif len(available) >= 1 and available[0]['quantity'] < escrow_quantity:
        problems.append('address doesn\'t has enough balance of %s (%i < %i)' % (asset_, available[0]['quantity'], escrow_quantity))
    else:
        cursor.execute('''SELECT * FROM dispensers WHERE source = ? AND asset = ? AND status=?''', (source, asset, STATUS_OPEN))
        open_dispensers = cursor.fetchall()
        if status == STATUS_OPEN:
            if len(open_dispensers) > 0 and open_dispensers[0]['satoshirate'] != mainchainrate:
                problems.append('address has a dispenser already opened for asset %s with a different mainchainrate' % asset_)

            if len(open_dispensers) > 0 and open_dispensers[0]['give_quantity'] != give_quantity:
                problems.append('address has a dispenser already opened for asset %s with a different give_quantity' % asset_)
        elif status == STATUS_CLOSED:
            if len(open_dispensers) == 0:
                problems.append('address doesnt has an open dispenser for asset %s' % asset_)

        if len(problems) == 0:
            asset_id = util.generate_asset_id(asset, block_index)
            if asset_id == 0:
                problems.append('cannot dispense %s' % asset_) # How can we test this on a test vector?

    if give_quantity > config.MAX_INT or escrow_quantity > config.MAX_INT or mainchainrate > config.MAX_INT:
        problems.append('integer overflow')

    if len(problems) > 0:
        return None, problems
    else:
        return asset_id, None
Example #2
0
def validate (db, source, asset, give_quantity, escrow_quantity, mainchainrate, status, block_index):
    problems = []
    order_match = None
    asset_id = None

    if asset == config.BTC:
        problems.append('cannot dispense %s' % config.BTC)
        return None, problems

    if escrow_quantity < give_quantity:
        problems.append('escrow_quantity must be greater or equal than give_quantity')

    if not(status == STATUS_OPEN or status == STATUS_CLOSED):
        problems.append('invalid status %i' % status)

    cursor = db.cursor()
    cursor.execute('''SELECT quantity FROM balances \
                      WHERE address = ? and asset = ?''', (source,asset,))
    available = cursor.fetchall()

    if len(available) == 0:
        problems.append('address doesn\'t has the asset %s' % asset)
    elif len(available) >= 1 and available[0]['quantity'] < escrow_quantity:
        problems.append('address doesn\'t has enough balance of %s (%i < %i)' % (asset, available[0]['quantity'], escrow_quantity))
    else:
        cursor.execute('''SELECT * FROM dispensers WHERE source = ? AND asset = ? AND status=?''', (source, asset, STATUS_OPEN))
        open_dispensers = cursor.fetchall()
        if status == STATUS_OPEN:
            if len(open_dispensers) > 0 and open_dispensers[0]['satoshirate'] != mainchainrate:
                problems.append('address has a dispenser already opened for asset %s with a different mainchainrate' % asset)

            if len(open_dispensers) > 0 and open_dispensers[0]['give_quantity'] != give_quantity:
                problems.append('address has a dispenser already opened for asset %s with a different give_quantity' % asset)
        elif status == STATUS_CLOSED:
            if len(open_dispensers) == 0:
                problems.append('address doesnt has an open dispenser for asset %s' % asset)

        if len(problems) == 0:
            asset_id = util.generate_asset_id(asset, block_index)
            if asset_id == 0:
                problems.append('cannot dispense %s' % asset) # How can we test this on a test vector?

    cursor.close()
    if len(problems) > 0:
        return None, problems
    else:
        return asset_id, None
Example #3
0
def compose(db, source, transfer_destination, asset, quantity, divisible,
            description):

    # Callability is depreciated, so for re‐issuances set relevant parameters
    # to old values; for first issuances, make uncallable.
    cursor = db.cursor()
    cursor.execute(
        '''SELECT * FROM issuances \
                      WHERE (status = ? AND asset = ?)
                      ORDER BY tx_index ASC''', ('valid', asset))
    issuances = cursor.fetchall()
    if issuances:
        last_issuance = issuances[-1]
        callable_ = last_issuance['callable']
        call_date = last_issuance['call_date']
        call_price = last_issuance['call_price']
    else:
        callable_ = False
        call_date = 0
        call_price = 0.0
    cursor.close()

    call_date, call_price, problems, fee, description, divisible, reissuance = validate(
        db, source, transfer_destination, asset, quantity, divisible,
        callable_, call_date, call_price, description,
        util.CURRENT_BLOCK_INDEX)
    if problems: raise exceptions.ComposeError(problems)

    asset_id = util.generate_asset_id(asset, util.CURRENT_BLOCK_INDEX)
    data = struct.pack(config.TXTYPE_FORMAT, ID)
    if len(description) <= 42:
        curr_format = FORMAT_2 + '{}p'.format(len(description) + 1)
    else:
        curr_format = FORMAT_2 + '{}s'.format(len(description))
    data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0,
                        1 if callable_ else 0, call_date or 0, call_price
                        or 0.0, description.encode('utf-8'))
    if transfer_destination:
        destination_outputs = [(transfer_destination, None)]
    else:
        destination_outputs = []
    return (source, destination_outputs, data)
Example #4
0
def compose (db, source, transfer_destination, asset, quantity, divisible, description):

    # Callability is deprecated, so for re‐issuances set relevant parameters
    # to old values; for first issuances, make uncallable.
    cursor = db.cursor()
    cursor.execute('''SELECT * FROM issuances \
                      WHERE (status = ? AND asset = ?)
                      ORDER BY tx_index ASC''', ('valid', asset))
    issuances = cursor.fetchall()
    if issuances:
        last_issuance = issuances[-1]
        callable_ = last_issuance['callable']
        call_date = last_issuance['call_date']
        call_price = last_issuance['call_price']
    else:
        callable_ = False
        call_date = 0
        call_price = 0.0
    cursor.close()

    call_date, call_price, problems, fee, description, divisible, reissuance = validate(db, source, transfer_destination, asset, quantity, divisible, callable_, call_date, call_price, description, util.CURRENT_BLOCK_INDEX)
    if problems: raise exceptions.ComposeError(problems)

    asset_id = util.generate_asset_id(asset, util.CURRENT_BLOCK_INDEX)
    data = struct.pack(config.TXTYPE_FORMAT, ID)
    if len(description) <= 42:
        curr_format = FORMAT_2 + '{}p'.format(len(description) + 1)
    else:
        curr_format = FORMAT_2 + '{}s'.format(len(description))
    data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if callable_ else 0,
        call_date or 0, call_price or 0.0, description.encode('utf-8'))
    if transfer_destination:
        destination_outputs = [(transfer_destination, None)]
    else:
        destination_outputs = []
    return (source, destination_outputs, data)
Example #5
0
def compose(db, source, transfer_destination, asset, quantity, divisible,
            description):

    # Callability is deprecated, so for re‐issuances set relevant parameters
    # to old values; for first issuances, make uncallable.
    cursor = db.cursor()
    cursor.execute(
        '''SELECT * FROM issuances \
                      WHERE (status = ? AND asset = ?)
                      ORDER BY tx_index ASC''', ('valid', asset))
    issuances = cursor.fetchall()
    if issuances:
        last_issuance = issuances[-1]
        callable_ = last_issuance['callable']
        call_date = last_issuance['call_date']
        call_price = last_issuance['call_price']
    else:
        callable_ = False
        call_date = 0
        call_price = 0.0
    cursor.close()

    # check subasset
    subasset_parent = None
    subasset_longname = None
    if util.enabled('subassets'):  # Protocol change.
        subasset_parent, subasset_longname = util.parse_subasset_from_asset_name(
            asset)
        if subasset_longname is not None:
            # try to find an existing subasset
            sa_cursor = db.cursor()
            sa_cursor.execute(
                '''SELECT * FROM assets \
                              WHERE (asset_longname = ?)''',
                (subasset_longname, ))
            assets = sa_cursor.fetchall()
            sa_cursor.close()
            if len(assets) > 0:
                # this is a reissuance
                asset = assets[0]['asset_name']
            else:
                # this is a new issuance
                #   generate a random numeric asset id which will map to this subasset
                asset = util.generate_random_asset()

    call_date, call_price, problems, fee, description, divisible, reissuance, reissued_asset_longname = validate(
        db, source, transfer_destination, asset, quantity, divisible,
        callable_, call_date, call_price, description, subasset_parent,
        subasset_longname, util.CURRENT_BLOCK_INDEX)
    if problems: raise exceptions.ComposeError(problems)

    asset_id = util.generate_asset_id(asset, util.CURRENT_BLOCK_INDEX)
    if subasset_longname is None or reissuance:
        # Type 20 standard issuance FORMAT_2 >QQ??If
        #   used for standard issuances and all reissuances
        data = message_type.pack(ID)
        if len(description) <= 42:
            curr_format = FORMAT_2 + '{}p'.format(len(description) + 1)
        else:
            curr_format = FORMAT_2 + '{}s'.format(len(description))
        data += struct.pack(curr_format, asset_id, quantity,
                            1 if divisible else 0, 1 if callable_ else 0,
                            call_date or 0, call_price or 0.0,
                            description.encode('utf-8'))
    else:
        # Type 21 subasset issuance SUBASSET_FORMAT >QQ?B
        #   Used only for initial subasset issuance
        # compacts a subasset name to save space
        compacted_subasset_longname = util.compact_subasset_longname(
            subasset_longname)
        compacted_subasset_length = len(compacted_subasset_longname)
        data = message_type.pack(SUBASSET_ID)
        curr_format = SUBASSET_FORMAT + '{}s'.format(
            compacted_subasset_length) + '{}s'.format(len(description))
        data += struct.pack(curr_format, asset_id, quantity,
                            1 if divisible else 0, compacted_subasset_length,
                            compacted_subasset_longname,
                            description.encode('utf-8'))

    if transfer_destination:
        destination_outputs = [(transfer_destination, None)]
    else:
        destination_outputs = []
    return (source, destination_outputs, data)
Example #6
0
def validate(db, source, asset, give_quantity, escrow_quantity, mainchainrate,
             status, open_address, block_index):
    problems = []
    order_match = None
    asset_id = None

    if asset == config.BTC:
        problems.append('cannot dispense %s' % config.BTC)
        return None, problems

    # resolve subassets
    asset = util.resolve_subasset_longname(db, asset)

    if status == STATUS_OPEN or status == STATUS_OPEN_EMPTY_ADDRESS:
        if give_quantity <= 0:
            problems.append('give_quantity must be positive')
        if mainchainrate <= 0:
            problems.append('mainchainrate must be positive')
        if escrow_quantity < give_quantity:
            problems.append(
                'escrow_quantity must be greater or equal than give_quantity')
    elif not (status == STATUS_CLOSED):
        problems.append('invalid status %i' % status)

    cursor = db.cursor()
    cursor.execute(
        '''SELECT quantity FROM balances \
                      WHERE address = ? and asset = ?''', (
            source,
            asset,
        ))
    available = cursor.fetchall()

    if len(available) == 0:
        problems.append('address doesn\'t has the asset %s' % asset)
    elif len(available) >= 1 and available[0]['quantity'] < escrow_quantity:
        problems.append('address doesn\'t has enough balance of %s (%i < %i)' %
                        (asset, available[0]['quantity'], escrow_quantity))
    else:
        if status == STATUS_OPEN_EMPTY_ADDRESS and not (open_address):
            open_address = source
            status = STATUS_OPEN

        query_address = open_address if status == STATUS_OPEN_EMPTY_ADDRESS else source
        cursor.execute(
            '''SELECT * FROM dispensers WHERE source = ? AND asset = ? AND status=?''',
            (query_address, asset, STATUS_OPEN))
        open_dispensers = cursor.fetchall()
        if status == STATUS_OPEN or status == STATUS_OPEN_EMPTY_ADDRESS:
            if len(open_dispensers) > 0 and open_dispensers[0][
                    'satoshirate'] != mainchainrate:
                problems.append(
                    'address has a dispenser already opened for asset %s with a different mainchainrate'
                    % asset)

            if len(open_dispensers) > 0 and open_dispensers[0][
                    'give_quantity'] != give_quantity:
                problems.append(
                    'address has a dispenser already opened for asset %s with a different give_quantity'
                    % asset)
        elif status == STATUS_CLOSED:
            if len(open_dispensers) == 0:
                problems.append(
                    'address doesnt has an open dispenser for asset %s' %
                    asset)

        if status == STATUS_OPEN_EMPTY_ADDRESS:
            cursor.execute(
                '''SELECT count(*) cnt FROM balances WHERE address = ?''',
                (query_address, ))
            existing_balances = cursor.fetchall()
            if existing_balances[0]['cnt'] > 0:
                problems.append(
                    'cannot open on another address if it has any balance history'
                )

        if len(problems) == 0:
            asset_id = util.generate_asset_id(asset, block_index)
            if asset_id == 0:
                problems.append(
                    'cannot dispense %s' %
                    asset)  # How can we test this on a test vector?

    cursor.close()
    if len(problems) > 0:
        return None, problems
    else:
        return asset_id, None
def compose (db, source, transfer_destination, asset, quantity, divisible, description):

    # Callability is deprecated, so for re‐issuances set relevant parameters
    # to old values; for first issuances, make uncallable.
    cursor = db.cursor()
    cursor.execute('''SELECT * FROM issuances \
                      WHERE (status = ? AND asset = ?)
                      ORDER BY tx_index ASC''', ('valid', asset))
    issuances = cursor.fetchall()
    if issuances:
        last_issuance = issuances[-1]
        callable_ = last_issuance['callable']
        call_date = last_issuance['call_date']
        call_price = last_issuance['call_price']
    else:
        callable_ = False
        call_date = 0
        call_price = 0.0
    cursor.close()

    # check subasset
    subasset_parent = None
    subasset_longname = None
    if util.enabled('subassets'): # Protocol change.
        subasset_parent, subasset_longname = util.parse_subasset_from_asset_name(asset)
        if subasset_longname is not None:
            # try to find an existing subasset
            sa_cursor = db.cursor()
            sa_cursor.execute('''SELECT * FROM assets \
                              WHERE (asset_longname = ?)''', (subasset_longname,))
            assets = sa_cursor.fetchall()
            sa_cursor.close()
            if len(assets) > 0:
                # this is a reissuance
                asset = assets[0]['asset_name']
            else:
                # this is a new issuance
                #   generate a random numeric asset id which will map to this subasset
                asset = util.generate_random_asset()

    call_date, call_price, problems, fee, description, divisible, reissuance, reissued_asset_longname = validate(db, source, transfer_destination, asset, quantity, divisible, callable_, call_date, call_price, description, subasset_parent, subasset_longname, util.CURRENT_BLOCK_INDEX)
    if problems: raise exceptions.ComposeError(problems)

    asset_id = util.generate_asset_id(asset, util.CURRENT_BLOCK_INDEX)
    if subasset_longname is None or reissuance:
        # Type 20 standard issuance FORMAT_2 >QQ??If
        #   used for standard issuances and all reissuances
        data = message_type.pack(ID)
        if len(description) <= 42:
            curr_format = FORMAT_2 + '{}p'.format(len(description) + 1)
        else:
            curr_format = FORMAT_2 + '{}s'.format(len(description))
        data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if callable_ else 0,
            call_date or 0, call_price or 0.0, description.encode('utf-8'))
    else:
        # Type 21 subasset issuance SUBASSET_FORMAT >QQ?B
        #   Used only for initial subasset issuance
        # compacts a subasset name to save space
        compacted_subasset_longname = util.compact_subasset_longname(subasset_longname)
        compacted_subasset_length = len(compacted_subasset_longname)
        data = message_type.pack(SUBASSET_ID)
        curr_format = SUBASSET_FORMAT + '{}s'.format(compacted_subasset_length) + '{}s'.format(len(description))
        data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, compacted_subasset_length, compacted_subasset_longname, description.encode('utf-8'))

    if transfer_destination:
        destination_outputs = [(transfer_destination, None)]
    else:
        destination_outputs = []
    return (source, destination_outputs, data)