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, asset, give_quantity, escrow_quantity, mainchainrate, status): assetid, problems = validate(db, source, asset, give_quantity, escrow_quantity, mainchainrate, status, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) data = message_type.pack(ID) data += struct.pack(FORMAT, assetid, give_quantity, escrow_quantity, mainchainrate, status) return (source, [], data)
def compose(db, source, asset_dest_quant_list, memo, memo_is_hex): cursor = db.cursor() out_balances = util.accumulate([(t[0], t[2]) for t in asset_dest_quant_list]) for (asset, quantity) in out_balances: # resolve subassets asset = util.resolve_subasset_longname(db, asset) if not isinstance(quantity, int): raise exceptions.ComposeError( 'quantities must be an int (in satoshis) for {}'.format(asset)) 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 for {}'.format(asset)) block_index = util.CURRENT_BLOCK_INDEX problems = validate(db, source, asset_dest_quant_list, block_index) if problems: raise exceptions.ComposeError(problems) data = message_type.pack(ID) data += _encode_mpmaSend(db, asset_dest_quant_list, block_index, memo=memo, memo_is_hex=memo_is_hex) return (source, [], data)
def compose(db, source, give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required): cursor = db.cursor() # resolve subassets give_asset = util.resolve_subasset_longname(db, give_asset) get_asset = util.resolve_subasset_longname(db, get_asset) # Check balance. if give_asset != config.BTC: balances = list( cursor.execute( '''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, give_asset))) if (not balances or balances[0]['quantity'] < give_quantity): raise exceptions.ComposeError('insufficient funds') problems = validate(db, source, give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) give_id = util.get_asset_id(db, give_asset, util.CURRENT_BLOCK_INDEX) get_id = util.get_asset_id(db, get_asset, util.CURRENT_BLOCK_INDEX) data = message_type.pack(ID) data += struct.pack(FORMAT, give_id, give_quantity, get_id, get_quantity, expiration, fee_required) cursor.close() return (source, [], data)
def pack(db, asset, quantity, tag): data = message_type.pack(ID) if type(tag) == 'str': tag = bytes.fromhex(tag) data += struct.pack(FORMAT, util.get_asset_id(db, asset, util.CURRENT_BLOCK_INDEX), quantity, tag) return data
def compose (db, source, asset, give_quantity, escrow_quantity, mainchainrate, status, open_address=None): assetid, problems = validate(db, source, asset, give_quantity, escrow_quantity, mainchainrate, status, open_address, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) data = message_type.pack(ID) data += struct.pack(FORMAT, assetid, give_quantity, escrow_quantity, mainchainrate, status) if status == STATUS_OPEN_EMPTY_ADDRESS and open_address: data += address.pack(open_address) return (source, [], data)
def compose(db, source, gasprice, startgas, endowment, code_hex): if not config.TESTNET: # TODO return data = message_type.pack(ID) data += struct.pack(FORMAT, gasprice, startgas, endowment) data += binascii.unhexlify(code_hex) return (source, [], data)
def compose(db, source, offer_hash): # Check that offer exists. _, _, problems = validate(db, source, offer_hash) if problems: raise exceptions.ComposeError(problems) offer_hash_bytes = binascii.unhexlify(bytes(offer_hash, 'utf-8')) data = message_type.pack(ID) data += struct.pack(FORMAT, offer_hash_bytes) return (source, [], data)
def compose(db, source, possible_moves, wager, move_random_hash, expiration): problems = validate(db, source, possible_moves, wager, move_random_hash, expiration, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) data = message_type.pack(ID) data += struct.pack(FORMAT, possible_moves, wager, binascii.unhexlify(move_random_hash), expiration) return (source, [], data)
def compose (db, source, offer_hash): # Check that offer exists. offer, offer_type, problems = validate(db, source, offer_hash) if problems: raise exceptions.ComposeError(problems) offer_hash_bytes = binascii.unhexlify(bytes(offer_hash, 'utf-8')) data = message_type.pack(ID) data += struct.pack(FORMAT, offer_hash_bytes) return (source, [], data)
def pack(db, asset, quantity, tag): data = message_type.pack(ID) if isinstance(tag, str): tag = bytes(tag.encode('utf8'))[0:MAX_TAG_LENGTH] elif isinstance(tag, bytes): tag = tag[0:MAX_TAG_LENGTH] else: tag = b'' data += struct.pack(FORMAT, util.get_asset_id(db, asset, util.CURRENT_BLOCK_INDEX), quantity) data += tag return data
def compose(db, source, destination, asset, quantity, memo, memo_is_hex): cursor = db.cursor() # Just send BTC? 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 BTC dust to the recipient return (source, [], data)
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_2, quantity_per_unit, asset_id, dividend_asset_id) return (source, [], data)
def compose (db, source, feed_address, bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration): if util.get_balance(db, source, config.XCP) < wager_quantity: raise exceptions.ComposeError('insufficient funds') problems, leverage = validate(db, source, feed_address, bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration, util.CURRENT_BLOCK_INDEX) if util.date_passed(deadline): problems.append('deadline passed') if problems: raise exceptions.ComposeError(problems) data = message_type.pack(ID) data += struct.pack(FORMAT, bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration) return (source, [(feed_address, None)], data)
def compose (db, source, move, random, rps_match_id): tx0_hash, tx1_hash = util.parse_id(rps_match_id) txn, rps_match, problems = validate(db, source, move, random, rps_match_id) if problems: raise exceptions.ComposeError(problems) # Warn if down to the wire. time_left = rps_match['match_expire_index'] - util.CURRENT_BLOCK_INDEX if time_left < 4: logger.warning('Only {} blocks until that rps match expires. The conclusion might not make into the blockchain in time.'.format(time_left)) tx0_hash_bytes = binascii.unhexlify(bytes(tx0_hash, 'utf-8')) tx1_hash_bytes = binascii.unhexlify(bytes(tx1_hash, 'utf-8')) random_bytes = binascii.unhexlify(bytes(random, 'utf-8')) data = message_type.pack(ID) data += struct.pack(FORMAT, move, random_bytes, tx0_hash_bytes, tx1_hash_bytes) return (source, [], data)
def compose (db, source, order_match_id): tx0_hash, tx1_hash = util.parse_id(order_match_id) destination, btc_quantity, escrowed_asset, escrowed_quantity, order_match, problems = validate(db, source, order_match_id, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) # Warn if down to the wire. time_left = order_match['match_expire_index'] - util.CURRENT_BLOCK_INDEX if time_left < 4: logger.warning('Only {} blocks until that order match expires. The payment might not make into the blockchain in time.'.format(time_left)) if 10 - time_left < 4: logger.warning('Order match has only {} confirmation(s).'.format(10 - time_left)) tx0_hash_bytes, tx1_hash_bytes = binascii.unhexlify(bytes(tx0_hash, 'utf-8')), binascii.unhexlify(bytes(tx1_hash, 'utf-8')) data = message_type.pack(ID) data += struct.pack(FORMAT, tx0_hash_bytes, tx1_hash_bytes) return (source, [(destination, btc_quantity)], data)
def compose(db, source, contract_id, gasprice, startgas, value, payload_hex): if not config.TESTNET: # TODO return payload = binascii.unhexlify(payload_hex) if startgas < 0: raise processblock.ContractError('negative startgas') if gasprice < 0: raise processblock.ContractError('negative gasprice') # Pack. data = message_type.pack(ID) curr_format = FORMAT + '{}s'.format(len(payload)) data += struct.pack(curr_format, binascii.unhexlify(contract_id), gasprice, startgas, value, payload) return (source, [], data)
def compose (db, source, destination, asset, quantity, memo, memo_is_hex): cursor = db.cursor() # Just send BTC? 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 BTC dust to the recipient return (source, [], data)
def compose(db, source, feed_address, bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration): if util.get_balance(db, source, config.XCP) < wager_quantity: raise exceptions.ComposeError('insufficient funds') problems, leverage = validate(db, source, feed_address, bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration, util.CURRENT_BLOCK_INDEX) if util.date_passed(deadline): problems.append('deadline passed') if problems: raise exceptions.ComposeError(problems) data = message_type.pack(ID) data += struct.pack(FORMAT, bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration) return (source, [(feed_address, None)], data)
def compose (db, source, destination, flags, memo): if memo is None: memo = b'' elif flags & FLAG_BINARY_MEMO: memo = bytes.fromhex(memo) else: memo = memo.encode('utf-8') memo = struct.pack(">{}s".format(len(memo)), memo) block_index = util.CURRENT_BLOCK_INDEX problems = validate(db, source, destination, flags, memo, block_index) if problems: raise exceptions.ComposeError(problems) short_address_bytes = address.pack(destination) data = message_type.pack(ID) data += struct.pack(FORMAT, short_address_bytes, flags) data += memo return (source, [], data)
def compose(db, source, move, random, rps_match_id): tx0_hash, tx1_hash = util.parse_id(rps_match_id) txn, rps_match, problems = validate(db, source, move, random, rps_match_id) if problems: raise exceptions.ComposeError(problems) # Warn if down to the wire. time_left = rps_match['match_expire_index'] - util.CURRENT_BLOCK_INDEX if time_left < 4: logger.warning( 'Only {} blocks until that rps match expires. The conclusion might not make into the blockchain in time.' .format(time_left)) tx0_hash_bytes = binascii.unhexlify(bytes(tx0_hash, 'utf-8')) tx1_hash_bytes = binascii.unhexlify(bytes(tx1_hash, 'utf-8')) random_bytes = binascii.unhexlify(bytes(random, 'utf-8')) data = message_type.pack(ID) data += struct.pack(FORMAT, move, random_bytes, tx0_hash_bytes, tx1_hash_bytes) return (source, [], data)
def compose(db, source, order_match_id): tx0_hash, tx1_hash = util.parse_id(order_match_id) destination, btc_quantity, _, _, order_match, problems = validate( db, source, order_match_id, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) # Warn if down to the wire. time_left = order_match['match_expire_index'] - util.CURRENT_BLOCK_INDEX if time_left < 4: logger.warning( 'Only {} blocks until that order match expires. The payment might not make into the blockchain in time.' .format(time_left)) if 10 - time_left < 4: logger.warning( 'Order match has only {} confirmation(s).'.format(10 - time_left)) tx0_hash_bytes, tx1_hash_bytes = binascii.unhexlify( bytes(tx0_hash, 'utf-8')), binascii.unhexlify(bytes(tx1_hash, 'utf-8')) data = message_type.pack(ID) data += struct.pack(FORMAT, tx0_hash_bytes, tx1_hash_bytes) return (source, [(destination, btc_quantity)], data)
def compose (db, source, give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required): cursor = db.cursor() # resolve subassets give_asset = util.resolve_subasset_longname(db, give_asset) get_asset = util.resolve_subasset_longname(db, get_asset) # Check balance. if give_asset != config.BTC: balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, give_asset))) if (not balances or balances[0]['quantity'] < give_quantity): raise exceptions.ComposeError('insufficient funds') problems = validate(db, source, give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) give_id = util.get_asset_id(db, give_asset, util.CURRENT_BLOCK_INDEX) get_id = util.get_asset_id(db, get_asset, util.CURRENT_BLOCK_INDEX) data = message_type.pack(ID) data += struct.pack(FORMAT, give_id, give_quantity, get_id, get_quantity, expiration, fee_required) cursor.close() 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 = 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 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 pack(db, asset, quantity, tag): data = message_type.pack(ID) data += struct.pack(FORMAT, util.get_asset_id(db, asset, util.CURRENT_BLOCK_INDEX), quantity, tag) return data
def pack(asset, quantity): data = message_type.pack(ID) data += struct.pack(FORMAT, util.asset_id(asset), quantity) return data