def compose(db, source, timestamp, value, fee_fraction, text): # Store the fee fraction as an integer. fee_fraction_int = int(fee_fraction * 1e8) problems = validate(db, source, timestamp, value, fee_fraction_int, text, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) data = message_type.pack(ID) # always use custom length byte instead of problematic usage of 52p format and make sure to encode('utf-8') for length if util.enabled('broadcast_pack_text'): data += struct.pack(FORMAT, timestamp, value, fee_fraction_int) data += VarIntSerializer.serialize(len(text.encode('utf-8'))) data += text.encode('utf-8') else: if len(text) <= 52: curr_format = FORMAT + '{}p'.format(len(text) + 1) else: curr_format = FORMAT + '{}s'.format(len(text)) data += struct.pack(curr_format, timestamp, value, fee_fraction_int, text.encode('utf-8')) return (source, [], data)
def compose(db, source, destination, asset, quantity, memo, memo_is_hex): cursor = db.cursor() # Just send GASP? if asset == config.BTC: return (source, [(destination, quantity)], None) # resolve subassets asset = util.resolve_subasset_longname(db, asset) # quantity must be in int satoshi (not float, string, etc) if not isinstance(quantity, int): raise exceptions.ComposeError('quantity must be an int (in satoshi)') # Only for outgoing (incoming will overburn). balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, asset))) if not balances or balances[0]['quantity'] < quantity: raise exceptions.ComposeError('insufficient funds') # convert memo to memo_bytes based on memo_is_hex setting if memo is None: memo_bytes = b'' elif memo_is_hex: memo_bytes = bytes.fromhex(memo) else: memo = memo.encode('utf-8') memo_bytes = struct.pack(">{}s".format(len(memo)), memo) block_index = util.CURRENT_BLOCK_INDEX problems = validate(db, source, destination, asset, quantity, memo_bytes, block_index) if problems: raise exceptions.ComposeError(problems) asset_id = util.get_asset_id(db, asset, block_index) short_address_bytes = address.pack(destination) data = message_type.pack(ID) data += struct.pack(FORMAT, asset_id, quantity, short_address_bytes) data += memo_bytes cursor.close() # return an empty array as the second argument because we don't need to send GASP dust to the recipient return (source, [], data)
def compose(db, source, destination, asset, quantity, memo=None, memo_is_hex=False, use_enhanced_send=None): # special case - enhanced_send replaces send by default when it is enabled # but it can be explicitly disabled with an API parameter if util.enabled('enhanced_sends'): if use_enhanced_send is None or use_enhanced_send is True: return enhanced_send.compose(db, source, destination, asset, quantity, memo, memo_is_hex) elif memo is not None or use_enhanced_send is True: raise exceptions.ComposeError('enhanced sends are not enabled') return send1.compose(db, source, destination, asset, quantity)
def compose(db, source, quantity_per_unit, asset, dividend_asset): # resolve subassets asset = util.resolve_subasset_longname(db, asset) dividend_asset = util.resolve_subasset_longname(db, dividend_asset) dividend_total, outputs, problems, fee = validate(db, source, quantity_per_unit, asset, dividend_asset, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) logger.info('Total quantity to be distributed in dividends: {} {}'.format( util.value_out(db, dividend_total, dividend_asset), dividend_asset)) if dividend_asset == config.BTC: return (source, [(output['address'], output['dividend_quantity']) for output in outputs], None) asset_id = util.get_asset_id(db, asset, util.CURRENT_BLOCK_INDEX) dividend_asset_id = util.get_asset_id(db, dividend_asset, util.CURRENT_BLOCK_INDEX) data = message_type.pack(ID) data += struct.pack(FORMAT, quantity_per_unit, asset_id, dividend_asset_id) return (source, [], 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, 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 compose(db, address, quantity): problems = validate(db, address, quantity, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) return address, quantity