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
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
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)
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)
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)
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)