示例#1
0
def parse (db, tx, message):
    status = 'valid'

    try:
        asset, quantity = unpack(message, tx['block_index'])
        validate(db, tx['source'], tx['destination'], asset, quantity)
        util.debit(db, tx['source'], asset, quantity, 'destroy', tx['tx_hash'])

    except UnpackError as e:
        asset, quantity = None, None
        status = 'invalid: ' + ''.join(e.args)

    except (ValidateError, BalanceError) as e:
        status = 'invalid: ' + ''.join(e.args)

    finally:
        bindings = {
                    'tx_index': tx['tx_index'],
                    'tx_hash': tx['tx_hash'],
                    'block_index': tx['block_index'],
                    'source': tx['source'],
                    'asset': asset,
                    'quantity': quantity,
                    'tag': tag,
                    'status': status,
                   }
        sql='insert into destructions values(:tx_index, :tx_hash, :block_index, :source, :asset, :quantity, :tag, :status)'
        cursor = db.cursor()
        cursor.execute(sql, bindings)
示例#2
0
def expire (db, block_index):
    cursor = db.cursor()

    # Expire rps and give refunds for the quantity wager.
    cursor.execute('''SELECT * FROM rps WHERE (status = ? AND expire_index < ?)''', ('open', block_index))
    for rps in cursor.fetchall():
        cancel_rps(db, rps, 'expired', block_index)

        # Record rps expiration.
        bindings = {
            'rps_index': rps['tx_index'],
            'rps_hash': rps['tx_hash'],
            'source': rps['source'],
            'block_index': block_index
        }
        sql = '''INSERT INTO rps_expirations VALUES (:rps_index, :rps_hash, :source, :block_index)'''
        cursor.execute(sql, bindings)

    # Expire rps matches
    expire_bindings = ('pending', 'pending and resolved', 'resolved and pending', block_index)
    cursor.execute('''SELECT * FROM rps_matches WHERE (status IN (?, ?, ?) AND match_expire_index < ?)''', expire_bindings)
    for rps_match in cursor.fetchall():

        new_rps_match_status = 'expired'
        # pending loses against resolved
        if rps_match['status'] == 'pending and resolved':
            new_rps_match_status = 'concluded: second player wins'
        elif rps_match['status'] == 'resolved and pending':
            new_rps_match_status = 'concluded: first player wins'
        update_rps_match_status(db, rps_match, new_rps_match_status, block_index)

        # Record rps match expiration.
        bindings = {
            'rps_match_id': rps_match['id'],
            'tx0_address': rps_match['tx0_address'],
            'tx1_address': rps_match['tx1_address'],
            'block_index': block_index
        }
        sql = '''INSERT INTO rps_match_expirations VALUES (:rps_match_id, :tx0_address, :tx1_address, :block_index)'''
        cursor.execute(sql, bindings)

        # Rematch not expired and not resolved RPS
        if new_rps_match_status == 'expired':
            sql = '''SELECT * FROM rps WHERE tx_hash IN (?, ?) AND status = ? AND expire_index >= ?'''
            bindings = (rps_match['tx0_hash'], rps_match['tx1_hash'], 'matched', block_index)
            matched_rps = list(cursor.execute(sql, bindings))
            for rps in matched_rps:
                cursor.execute('''UPDATE rps SET status = ? WHERE tx_index = ?''', ('open', rps['tx_index']))
                # Re-debit SHP refund by close_rps_match.
                util.debit(db, rps['source'], 'SHP', rps['wager'], action='reopen RPS after matching expiration', event=rps_match['id'])
                # Rematch
                match(db, {'tx_index': rps['tx_index']}, block_index)

    cursor.close()
示例#3
0
def parse(db, tx, message):
    rps_parse_cursor = db.cursor()
    # Unpack message.
    try:
        if len(message) != LENGTH:
            raise exceptions.UnpackError
        (possible_moves, wager, move_random_hash, expiration) = struct.unpack(FORMAT, message)
        status = 'open'
    except (exceptions.UnpackError, struct.error):
        (possible_moves, wager, move_random_hash, expiration) = 0, 0, '', 0
        status = 'invalid: could not unpack'

    if status == 'open':
        move_random_hash = binascii.hexlify(move_random_hash).decode('utf8')
        # Overbet
        rps_parse_cursor.execute('''SELECT * FROM balances \
                                    WHERE (address = ? AND asset = ?)''', (tx['source'], 'SHP'))
        balances = list(rps_parse_cursor)
        if not balances:
            wager = 0
        else:
            balance = balances[0]['quantity']
            if balance < wager:
                wager = balance

        problems = validate(db, tx['source'], possible_moves, wager, move_random_hash, expiration, tx['block_index'])
        if problems: status = 'invalid: {}'.format(', '.join(problems))

    # Debit quantity wagered. (Escrow.)
    if status == 'open':
        util.debit(db, tx['source'], 'SHP', wager, action="open RPS", event=tx['tx_hash'])

    # Add parsed transaction to message-type–specific table.
    bindings = {
        'tx_index': tx['tx_index'],
        'tx_hash': tx['tx_hash'],
        'block_index': tx['block_index'],
        'source': tx['source'],
        'possible_moves': possible_moves,
        'wager': wager,
        'move_random_hash': move_random_hash,
        'expiration': expiration,
        'expire_index': tx['block_index'] + expiration,
        'status': status,
    }
    sql = '''INSERT INTO rps VALUES (:tx_index, :tx_hash, :block_index, :source, :possible_moves, :wager, :move_random_hash, :expiration, :expire_index, :status)'''
    rps_parse_cursor.execute(sql, bindings)

    # Match.
    if status == 'open':
        match(db, tx, tx['block_index'])

    rps_parse_cursor.close()
示例#4
0
def parse(db, tx, message):
    bet_parse_cursor = db.cursor()

    # Unpack message.
    try:
        if len(message) != LENGTH:
            raise exceptions.UnpackError
        (bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration) = struct.unpack(
            FORMAT, message
        )
        status = "open"
    except (exceptions.UnpackError, struct.error):
        (
            bet_type,
            deadline,
            wager_quantity,
            counterwager_quantity,
            target_value,
            leverage,
            expiration,
            fee_fraction_int,
        ) = (0, 0, 0, 0, 0, 0, 0, 0)
        status = "invalid: could not unpack"

    odds, fee_fraction = 0, 0
    feed_address = tx["destination"]
    if status == "open":
        try:
            odds = util.price(wager_quantity, counterwager_quantity)
        except ZeroDivisionError:
            odds = 0

        fee_fraction = get_fee_fraction(db, feed_address)

        # Overbet
        bet_parse_cursor.execute(
            """SELECT * FROM balances \
                                    WHERE (address = ? AND asset = ?)""",
            (tx["source"], config.SHP),
        )
        balances = list(bet_parse_cursor)
        if not balances:
            wager_quantity = 0
        else:
            balance = balances[0]["quantity"]
            if balance < wager_quantity:
                wager_quantity = balance
                counterwager_quantity = int(util.price(wager_quantity, odds))

        problems, leverage = validate(
            db,
            tx["source"],
            feed_address,
            bet_type,
            deadline,
            wager_quantity,
            counterwager_quantity,
            target_value,
            leverage,
            expiration,
            tx["block_index"],
        )
        if problems:
            status = "invalid: " + "; ".join(problems)

    # Debit quantity wagered. (Escrow.)
    if status == "open":
        util.debit(db, tx["source"], config.SHP, wager_quantity, action="bet", event=tx["tx_hash"])

    # Add parsed transaction to message-type–specific table.
    bindings = {
        "tx_index": tx["tx_index"],
        "tx_hash": tx["tx_hash"],
        "block_index": tx["block_index"],
        "source": tx["source"],
        "feed_address": feed_address,
        "bet_type": bet_type,
        "deadline": deadline,
        "wager_quantity": wager_quantity,
        "wager_remaining": wager_quantity,
        "counterwager_quantity": counterwager_quantity,
        "counterwager_remaining": counterwager_quantity,
        "target_value": target_value,
        "leverage": leverage,
        "expiration": expiration,
        "expire_index": tx["block_index"] + expiration,
        "fee_fraction_int": fee_fraction * 1e8,
        "status": status,
    }
    sql = "insert into bets values(:tx_index, :tx_hash, :block_index, :source, :feed_address, :bet_type, :deadline, :wager_quantity, :wager_remaining, :counterwager_quantity, :counterwager_remaining, :target_value, :leverage, :expiration, :expire_index, :fee_fraction_int, :status)"
    bet_parse_cursor.execute(sql, bindings)

    # Match.
    if status == "open" and tx["block_index"] != config.MEMPOOL_BLOCK_INDEX:
        match(db, tx)

    bet_parse_cursor.close()
示例#5
0
def parse (db, tx, message):
    issuance_parse_cursor = db.cursor()

    # Unpack message.
    try:
        if (tx['block_index'] > 283271 or config.TESTNET) and len(message) >= LENGTH_2: # Protocol change.
            if len(message) - LENGTH_2 <= 42:
                curr_format = FORMAT_2 + '{}p'.format(len(message) - LENGTH_2)
            else:
                curr_format = FORMAT_2 + '{}s'.format(len(message) - LENGTH_2)
            asset_id, quantity, divisible, callable_, call_date, call_price, description = struct.unpack(curr_format, message)

            call_price = round(call_price, 6) # TODO: arbitrary
            try:
                description = description.decode('utf-8')
            except UnicodeDecodeError:
                description = ''
        else:
            if len(message) != LENGTH_1:
                raise exceptions.UnpackError
            asset_id, quantity, divisible = struct.unpack(FORMAT_1, message)
            callable_, call_date, call_price, description = False, 0, 0.0, ''
        try:
            asset = util.generate_asset_name(asset_id, tx['block_index'])
        except exceptions.AssetNameError:
            asset = None
            status = 'invalid: bad asset name'
        status = 'valid'
    except exceptions.UnpackError as e:
        asset, quantity, divisible, callable_, call_date, call_price, description = None, None, None, None, None, None, None
        status = 'invalid: could not unpack'

    fee = 0
    if status == 'valid':
        call_date, call_price, problems, fee, description, divisible, reissuance = validate(db, tx['source'], tx['destination'], asset, quantity, divisible, callable_, call_date, call_price, description, block_index=tx['block_index'])
        if problems: status = 'invalid: ' + '; '.join(problems)
        if 'total quantity overflow' in problems:
            quantity = 0

    if tx['destination']:
        issuer = tx['destination']
        transfer = True
        quantity = 0
    else:
        issuer = tx['source']
        transfer = False

    # Debit fee.
    if status == 'valid':
        util.debit(db, tx['source'], config.SHP, fee, action="issuance fee", event=tx['tx_hash'])

    # Lock?
    lock = False
    if status == 'valid':
        if description and description.lower() == 'lock':
            lock = True
            cursor = db.cursor()
            issuances = list(cursor.execute('''SELECT * FROM issuances \
                                               WHERE (status = ? AND asset = ?)
                                               ORDER BY tx_index ASC''', ('valid', asset)))
            cursor.close()
            description = issuances[-1]['description']  # Use last description. (Assume previous issuance exists because tx is valid.)
            timestamp, value_int, fee_fraction_int = None, None, None

        if not reissuance:
            # Add to table of assets.
            bindings= {
                'asset_id': str(asset_id),
                'asset_name': str(asset),
                'block_index': tx['block_index'],
            }
            sql='insert into assets values(:asset_id, :asset_name, :block_index)'
            issuance_parse_cursor.execute(sql, bindings)

    # Add parsed transaction to message-type–specific table.
    bindings= {
        'tx_index': tx['tx_index'],
        'tx_hash': tx['tx_hash'],
        'block_index': tx['block_index'],
        'asset': asset,
        'quantity': quantity,
        'divisible': divisible,
        'source': tx['source'],
        'issuer': issuer,
        'transfer': transfer,
        'callable': callable_,
        'call_date': call_date,
        'call_price': call_price,
        'description': description,
        'fee_paid': fee,
        'locked': lock,
        'status': status,
    }
    sql='insert into issuances values(:tx_index, :tx_hash, :block_index, :asset, :quantity, :divisible, :source, :issuer, :transfer, :callable, :call_date, :call_price, :description, :fee_paid, :locked, :status)'
    issuance_parse_cursor.execute(sql, bindings)

    # Credit.
    if status == 'valid' and quantity:
        util.credit(db, tx['source'], asset, quantity, action="issuance", event=tx['tx_hash'])

    issuance_parse_cursor.close()
示例#6
0
def parse(db, tx, message):
    order_parse_cursor = db.cursor()

    # Unpack message.
    try:
        if len(message) != LENGTH:
            raise exceptions.UnpackError
        give_id, give_quantity, get_id, get_quantity, expiration, fee_required = struct.unpack(FORMAT, message)
        give_asset = util.get_asset_name(db, give_id, tx["block_index"])
        get_asset = util.get_asset_name(db, get_id, tx["block_index"])
        status = "open"
    except (exceptions.UnpackError, exceptions.AssetNameError, struct.error) as e:
        give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required = 0, 0, 0, 0, 0, 0
        status = "invalid: could not unpack"

    price = 0
    if status == "open":
        try:
            price = util.price(get_quantity, give_quantity)
        except ZeroDivisionError:
            price = 0

        # Overorder
        order_parse_cursor.execute(
            """SELECT * FROM balances \
                                      WHERE (address = ? AND asset = ?)""",
            (tx["source"], give_asset),
        )
        balances = list(order_parse_cursor)
        if give_asset != config.SCH:
            if not balances:
                give_quantity = 0
            else:
                balance = balances[0]["quantity"]
                if balance < give_quantity:
                    give_quantity = balance
                    get_quantity = int(price * give_quantity)

        problems = validate(
            db,
            tx["source"],
            give_asset,
            give_quantity,
            get_asset,
            get_quantity,
            expiration,
            fee_required,
            tx["block_index"],
        )
        if problems:
            status = "invalid: " + "; ".join(problems)

    # Debit give quantity. (Escrow.)
    if status == "open":
        if give_asset != config.SCH:  # No need (or way) to debit SCH.
            util.debit(db, tx["source"], give_asset, give_quantity, action="open order", event=tx["tx_hash"])

    # Add parsed transaction to message-type–specific table.
    bindings = {
        "tx_index": tx["tx_index"],
        "tx_hash": tx["tx_hash"],
        "block_index": tx["block_index"],
        "source": tx["source"],
        "give_asset": give_asset,
        "give_quantity": give_quantity,
        "give_remaining": give_quantity,
        "get_asset": get_asset,
        "get_quantity": get_quantity,
        "get_remaining": get_quantity,
        "expiration": expiration,
        "expire_index": tx["block_index"] + expiration,
        "fee_required": fee_required,
        "fee_required_remaining": fee_required,
        "fee_provided": tx["fee"],
        "fee_provided_remaining": tx["fee"],
        "status": status,
    }
    sql = "insert into orders values(:tx_index, :tx_hash, :block_index, :source, :give_asset, :give_quantity, :give_remaining, :get_asset, :get_quantity, :get_remaining, :expiration, :expire_index, :fee_required, :fee_required_remaining, :fee_provided, :fee_provided_remaining, :status)"
    order_parse_cursor.execute(sql, bindings)

    # Match.
    if status == "open" and tx["block_index"] != config.MEMPOOL_BLOCK_INDEX:
        match(db, tx)

    order_parse_cursor.close()
示例#7
0
 def transfer_value(self, tx, source, destination, quantity, asset=config.SHP):
     if source:
         util.debit(self.db, source, asset, quantity, action='transfer value', event=tx.tx_hash)
     if destination:
         util.credit(self.db, destination, asset, quantity, action='transfer value', event=tx.tx_hash)
     return True