Esempio n. 1
0
def cancel_order(db, order, status, block_index):
    cursor = db.cursor()

    # Update status of order.
    bindings = {"status": status, "tx_hash": order["tx_hash"]}
    sql = "update orders set status = :status where tx_hash = :tx_hash"
    cursor.execute(sql, bindings)
    log.message(db, block_index, "update", "orders", bindings)

    if order["give_asset"] != config.SCH:  # Can’t credit SCH.
        util.credit(
            db,
            order["source"],
            order["give_asset"],
            order["give_remaining"],
            action="cancel order",
            event=order["tx_hash"],
        )

    if status == "expired":
        # Record offer expiration.
        bindings = {
            "order_index": order["tx_index"],
            "order_hash": order["tx_hash"],
            "source": order["source"],
            "block_index": block_index,
        }
        sql = "insert into order_expirations values(:order_index, :order_hash, :source, :block_index)"
        cursor.execute(sql, bindings)

    cursor.close()
Esempio n. 2
0
def cancel_bet_match(db, bet_match, status, block_index):
    # Does not re‐open, re‐fill, etc. constituent bets.

    cursor = db.cursor()

    # Recredit tx0 address.
    util.credit(
        db,
        bet_match["tx0_address"],
        config.SHP,
        bet_match["forward_quantity"],
        action="recredit forward quantity",
        event=bet_match["id"],
    )

    # Recredit tx1 address.
    util.credit(
        db,
        bet_match["tx1_address"],
        config.SHP,
        bet_match["backward_quantity"],
        action="recredit backward quantity",
        event=bet_match["id"],
    )

    # Update status of bet match.
    bindings = {"status": status, "bet_match_id": bet_match["id"]}
    sql = "update bet_matches set status = :status where id = :bet_match_id"
    cursor.execute(sql, bindings)
    log.message(db, block_index, "update", "bet_matches", bindings)

    cursor.close()
        def create_contract(self, code, endowment=0, sender=''):
            if not sender:
                sender = '82a978b3f5962a5b0957d9ee9eef472ee55b42f1' # PyEthereum uses ECDSA to derive this string from `sender = 0`.

            util.credit(db, sender, config.SHP, max(endowment*2, 100000000), action='unit test', event='facefeed')

            success, data = tester.state.do_send(self, sender, '', endowment, data=code)
            contract_id = data
            return contract_id
Esempio n. 4
0
def parse (db, tx, message):
    cursor = db.cursor()

    # Unpack message.
    try:
        if len(message) != LENGTH:
            raise exceptions.UnpackError
        tx0_hash_bytes, tx1_hash_bytes = struct.unpack(FORMAT, message)
        tx0_hash, tx1_hash = binascii.hexlify(tx0_hash_bytes).decode('utf-8'), binascii.hexlify(tx1_hash_bytes).decode('utf-8')
        order_match_id = util.make_id(tx0_hash, tx1_hash)
        status = 'valid'
    except (exceptions.UnpackError, struct.error) as e:
        tx0_hash, tx1_hash, order_match_id = None, None, None
        status = 'invalid: could not unpack'

    if status == 'valid':
        destination, shell_quantity, escrowed_asset, escrowed_quantity, order_match, problems = validate(db, tx['source'], order_match_id, tx['block_index'])
        if problems:
            order_match = None
            status = 'invalid: ' + '; '.join(problems)

    if status == 'valid':
        # SCH must be paid all at once.
        if tx['shell_amount'] >= shell_quantity:

            # Credit source address for the currency that he bought with the SatoshiChains.
            util.credit(db, tx['source'], escrowed_asset, escrowed_quantity, action='shellpay', event=tx['tx_hash'])
            status = 'valid'

            # Update order match.
            bindings = {
                'status': 'completed',
                'order_match_id': order_match_id
            }
            sql='update order_matches set status = :status where id = :order_match_id'
            cursor.execute(sql, bindings)
            log.message(db, tx['block_index'], 'update', 'order_matches', 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'],
        'source': tx['source'],
        'destination': tx['destination'],
        'shell_amount': tx['shell_amount'],
        'order_match_id': order_match_id,
        'status': status,
    }
    sql='insert into shellpays values(:tx_index, :tx_hash, :block_index, :source, :destination, :shell_amount, :order_match_id, :status)'
    cursor.execute(sql, bindings)


    cursor.close()
Esempio n. 5
0
def cancel_bet(db, bet, status, block_index):
    cursor = db.cursor()

    # Update status of bet.
    bindings = {"status": status, "tx_hash": bet["tx_hash"]}
    sql = "update bets set status = :status where tx_hash = :tx_hash"
    cursor.execute(sql, bindings)
    log.message(db, block_index, "update", "bets", bindings)

    util.credit(
        db, bet["source"], config.SHP, bet["wager_remaining"], action="recredit wager remaining", event=bet["tx_hash"]
    )

    cursor = db.cursor()
        def send (self, sender, to, value, data=[]):
            # print('tuple', sender, to, value, data)

            # Encode data.
            data = serpent.encode_datalist(data)
            data = binascii.unhexlify(data)

            # Execute contract.
            # print('qux', data, type(data))
            util.credit(db, sender, config.SHP, value + 100000000, action='unit test', event='facefeed')
            success, output = tester.state.do_send(self, sender, to, value, data=data)
            if output:
                return rlp.decode_datalist(bytes(output))
            else:
                return []
Esempio n. 7
0
def cancel_rps (db, rps, status, block_index):
    cursor = db.cursor()

    # Update status of rps.
    bindings = {
        'status': status,
        'tx_hash': rps['tx_hash']
    }
    sql='''UPDATE rps SET status = :status WHERE tx_hash = :tx_hash'''
    cursor.execute(sql, bindings)
    log.message(db, block_index, 'update', 'rps', bindings)

    util.credit(db, rps['source'], 'SHP', rps['wager'], action='recredit wager', event=rps['tx_hash'])

    cursor.close()
Esempio n. 8
0
def update_rps_match_status (db, rps_match, status, block_index):
    cursor = db.cursor()

    if status in ['expired', 'concluded: tie']:
        # Recredit tx0 address.
        util.credit(db, rps_match['tx0_address'], 'SHP',
                    rps_match['wager'], action='recredit wager', event=rps_match['id'])
        # Recredit tx1 address.
        util.credit(db, rps_match['tx1_address'], 'SHP',
                    rps_match['wager'], action='recredit wager', event=rps_match['id'])
    elif status.startswith('concluded'):
        # Credit the winner
        winner = rps_match['tx0_address'] if status == 'concluded: first player wins' else rps_match['tx1_address']
        util.credit(db, winner, 'SHP',
                    2 * rps_match['wager'], action='wins', event=rps_match['id'])

    # Update status of rps match.
    bindings = {
        'status': status,
        'rps_match_id': rps_match['id']
    }
    sql='UPDATE rps_matches SET status = :status WHERE id = :rps_match_id'
    cursor.execute(sql, bindings)
    log.message(db, block_index, 'update', 'rps_matches', bindings)

    cursor.close()
Esempio n. 9
0
def match(db, tx):

    cursor = db.cursor()

    # Get bet in question.
    bets = list(
        cursor.execute(
            """SELECT * FROM bets\
                                  WHERE (tx_index = ? AND status = ?)""",
            (tx["tx_index"], "open"),
        )
    )
    if not bets:
        cursor.close()
        return
    else:
        assert len(bets) == 1
    tx1 = bets[0]

    # Get counterbet_type.
    if tx1["bet_type"] % 2:
        counterbet_type = tx1["bet_type"] - 1
    else:
        counterbet_type = tx1["bet_type"] + 1

    feed_address = tx1["feed_address"]

    cursor.execute(
        """SELECT * FROM bets\
                             WHERE (feed_address=? AND status=? AND bet_type=?)""",
        (tx1["feed_address"], "open", counterbet_type),
    )
    tx1_wager_remaining = tx1["wager_remaining"]
    tx1_counterwager_remaining = tx1["counterwager_remaining"]
    bet_matches = cursor.fetchall()
    if tx["block_index"] > 284500 or config.TESTNET:  # Protocol change.
        sorted(bet_matches, key=lambda x: x["tx_index"])  # Sort by tx index second.
        sorted(
            bet_matches, key=lambda x: util.price(x["wager_quantity"], x["counterwager_quantity"])
        )  # Sort by price first.

    tx1_status = tx1["status"]
    for tx0 in bet_matches:
        if tx1_status != "open":
            break

        logger.debug("Considering: " + tx0["tx_hash"])
        tx0_wager_remaining = tx0["wager_remaining"]
        tx0_counterwager_remaining = tx0["counterwager_remaining"]

        # Bet types must be opposite.
        if counterbet_type != tx0["bet_type"]:
            logger.debug("Skipping: bet types disagree.")
            continue

        # Leverages must agree exactly
        if tx0["leverage"] != tx1["leverage"]:
            logger.debug("Skipping: leverages disagree.")
            continue

        # Target values must agree exactly.
        if tx0["target_value"] != tx1["target_value"]:
            logger.debug("Skipping: target values disagree.")
            continue

        # Fee fractions must agree exactly.
        if tx0["fee_fraction_int"] != tx1["fee_fraction_int"]:
            logger.debug("Skipping: fee fractions disagree.")
            continue

        # Deadlines must agree exactly.
        if tx0["deadline"] != tx1["deadline"]:
            logger.debug("Skipping: deadlines disagree.")
            continue

        # If the odds agree, make the trade. The found order sets the odds,
        # and they trade as much as they can.
        tx0_odds = util.price(tx0["wager_quantity"], tx0["counterwager_quantity"])
        tx0_inverse_odds = util.price(tx0["counterwager_quantity"], tx0["wager_quantity"])
        tx1_odds = util.price(tx1["wager_quantity"], tx1["counterwager_quantity"])

        if tx["block_index"] < 286000:
            tx0_inverse_odds = util.price(1, tx0_odds)  # Protocol change.

        logger.debug("Tx0 Inverse Odds: {}; Tx1 Odds: {}".format(float(tx0_inverse_odds), float(tx1_odds)))
        if tx0_inverse_odds > tx1_odds:
            logger.debug("Skipping: price mismatch.")
        else:
            logger.debug(
                "Potential forward quantities: {}, {}".format(
                    tx0_wager_remaining, int(util.price(tx1_wager_remaining, tx1_odds))
                )
            )
            forward_quantity = int(min(tx0_wager_remaining, int(util.price(tx1_wager_remaining, tx1_odds))))
            logger.debug("Forward Quantity: {}".format(forward_quantity))
            backward_quantity = round(forward_quantity / tx0_odds)
            logger.debug("Backward Quantity: {}".format(backward_quantity))

            if not forward_quantity:
                logger.debug("Skipping: zero forward quantity.")
                continue
            if tx1["block_index"] >= 286500 or config.TESTNET:  # Protocol change.
                if not backward_quantity:
                    logger.debug("Skipping: zero backward quantity.")
                    continue

            bet_match_id = util.make_id(tx0["tx_hash"], tx1["tx_hash"])

            # Debit the order.
            # Counterwager remainings may be negative.
            tx0_wager_remaining = tx0_wager_remaining - forward_quantity
            tx0_counterwager_remaining = tx0_counterwager_remaining - backward_quantity
            tx1_wager_remaining = tx1_wager_remaining - backward_quantity
            tx1_counterwager_remaining = tx1_counterwager_remaining - forward_quantity

            # tx0
            tx0_status = "open"
            if tx0_wager_remaining <= 0 or tx0_counterwager_remaining <= 0:
                # Fill order, and recredit give_remaining.
                tx0_status = "filled"
                util.credit(db, tx0["source"], config.SHP, tx0_wager_remaining, event=tx1["tx_hash"], action="filled")
            bindings = {
                "wager_remaining": tx0_wager_remaining,
                "counterwager_remaining": tx0_counterwager_remaining,
                "status": tx0_status,
                "tx_hash": tx0["tx_hash"],
            }
            sql = "update bets set wager_remaining = :wager_remaining, counterwager_remaining = :counterwager_remaining, status = :status where tx_hash = :tx_hash"
            cursor.execute(sql, bindings)
            log.message(db, tx["block_index"], "update", "bets", bindings)

            if tx1["block_index"] >= 292000 or config.TESTNET:  # Protocol change
                if tx1_wager_remaining <= 0 or tx1_counterwager_remaining <= 0:
                    # Fill order, and recredit give_remaining.
                    tx1_status = "filled"
                    util.credit(
                        db, tx1["source"], config.SHP, tx1_wager_remaining, event=tx1["tx_hash"], action="filled"
                    )
            # tx1
            bindings = {
                "wager_remaining": tx1_wager_remaining,
                "counterwager_remaining": tx1_counterwager_remaining,
                "status": tx1_status,
                "tx_hash": tx1["tx_hash"],
            }
            sql = "update bets set wager_remaining = :wager_remaining, counterwager_remaining = :counterwager_remaining, status = :status where tx_hash = :tx_hash"
            cursor.execute(sql, bindings)
            log.message(db, tx["block_index"], "update", "bets", bindings)

            # Get last value of feed.
            broadcasts = list(
                cursor.execute(
                    """SELECT * FROM broadcasts WHERE (status = ? AND source = ?) ORDER BY tx_index ASC""",
                    ("valid", feed_address),
                )
            )
            initial_value = broadcasts[-1]["value"]

            # Record bet fulfillment.
            bindings = {
                "id": util.make_id(tx0["tx_hash"], tx["tx_hash"]),
                "tx0_index": tx0["tx_index"],
                "tx0_hash": tx0["tx_hash"],
                "tx0_address": tx0["source"],
                "tx1_index": tx1["tx_index"],
                "tx1_hash": tx1["tx_hash"],
                "tx1_address": tx1["source"],
                "tx0_bet_type": tx0["bet_type"],
                "tx1_bet_type": tx1["bet_type"],
                "feed_address": tx1["feed_address"],
                "initial_value": initial_value,
                "deadline": tx1["deadline"],
                "target_value": tx1["target_value"],
                "leverage": tx1["leverage"],
                "forward_quantity": forward_quantity,
                "backward_quantity": backward_quantity,
                "tx0_block_index": tx0["block_index"],
                "tx1_block_index": tx1["block_index"],
                "block_index": max(tx0["block_index"], tx1["block_index"]),
                "tx0_expiration": tx0["expiration"],
                "tx1_expiration": tx1["expiration"],
                "match_expire_index": min(tx0["expire_index"], tx1["expire_index"]),
                "fee_fraction_int": tx1["fee_fraction_int"],
                "status": "pending",
            }
            sql = "insert into bet_matches values(:id, :tx0_index, :tx0_hash, :tx0_address, :tx1_index, :tx1_hash, :tx1_address, :tx0_bet_type, :tx1_bet_type, :feed_address, :initial_value, :deadline, :target_value, :leverage, :forward_quantity, :backward_quantity, :tx0_block_index, :tx1_block_index, :block_index, :tx0_expiration, :tx1_expiration, :match_expire_index, :fee_fraction_int, :status)"
            cursor.execute(sql, bindings)

    cursor.close()
    return
Esempio n. 10
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()
Esempio n. 11
0
def match(db, tx, block_index=None):

    cursor = db.cursor()

    # Get order in question.
    orders = list(
        cursor.execute(
            """SELECT * FROM orders\
                                    WHERE (tx_index = ? AND status = ?)""",
            (tx["tx_index"], "open"),
        )
    )
    if not orders:
        cursor.close()
        return
    else:
        assert len(orders) == 1
    tx1 = orders[0]

    cursor.execute(
        """SELECT * FROM orders \
                      WHERE (give_asset=? AND get_asset=? AND status=? AND tx_hash != ?)""",
        (tx1["get_asset"], tx1["give_asset"], "open", tx1["tx_hash"]),
    )

    tx1_give_remaining = tx1["give_remaining"]
    tx1_get_remaining = tx1["get_remaining"]

    order_matches = cursor.fetchall()
    if tx["block_index"] > 284500 or config.TESTNET:  # Protocol change.
        order_matches = sorted(order_matches, key=lambda x: x["tx_index"])  # Sort by tx index second.
        order_matches = sorted(
            order_matches, key=lambda x: util.price(x["get_quantity"], x["give_quantity"])
        )  # Sort by price first.

    # Get fee remaining.
    tx1_fee_required_remaining = tx1["fee_required_remaining"]
    tx1_fee_provided_remaining = tx1["fee_provided_remaining"]

    tx1_status = tx1["status"]
    for tx0 in order_matches:
        order_match_id = util.make_id(tx0["tx_hash"], tx1["tx_hash"])
        if not block_index:
            block_index = max(tx0["block_index"], tx1["block_index"])
        if tx1_status != "open":
            break

        logger.debug("Considering: " + tx0["tx_hash"])
        tx0_give_remaining = tx0["give_remaining"]
        tx0_get_remaining = tx0["get_remaining"]

        # Ignore previous matches. (Both directions, just to be sure.)
        cursor.execute(
            """SELECT * FROM order_matches
                          WHERE id = ? """,
            (util.make_id(tx0["tx_hash"], tx1["tx_hash"]),),
        )
        if list(cursor):
            logger.debug("Skipping: previous match")
            continue
        cursor.execute(
            """SELECT * FROM order_matches
                          WHERE id = ? """,
            (util.make_id(tx1["tx_hash"], tx0["tx_hash"]),),
        )
        if list(cursor):
            logger.debug("Skipping: previous match")
            continue

        # Get fee provided remaining.
        tx0_fee_required_remaining = tx0["fee_required_remaining"]
        tx0_fee_provided_remaining = tx0["fee_provided_remaining"]

        # Make sure that that both orders still have funds remaining (if order involves SCH, and so cannot be ‘filled’).
        if tx0["give_asset"] == config.SCH or tx0["get_asset"] == config.SCH:  # Gratuitous
            if tx0_give_remaining <= 0 or tx1_give_remaining <= 0:
                logger.debug("Skipping: negative give quantity remaining")
                continue
            if block_index >= 292000 and block_index <= 310500 and not config.TESTNET:  # Protocol changes
                if tx0_get_remaining <= 0 or tx1_get_remaining <= 0:
                    logger.debug("Skipping: negative get quantity remaining")
                    continue

            if block_index >= 294000 or config.TESTNET:  # Protocol change.
                if tx0["fee_required_remaining"] < 0:
                    logger.debug("Skipping: negative tx0 fee required remaining")
                    continue
                if tx0["fee_provided_remaining"] < 0:
                    logger.debug("Skipping: negative tx0 fee provided remaining")
                    continue
                if tx1_fee_provided_remaining < 0:
                    logger.debug("Skipping: negative tx1 fee provided remaining")
                    continue
                if tx1_fee_required_remaining < 0:
                    logger.debug("Skipping: negative tx1 fee required remaining")
                    continue

        # If the prices agree, make the trade. The found order sets the price,
        # and they trade as much as they can.
        tx0_price = util.price(tx0["get_quantity"], tx0["give_quantity"])
        tx1_price = util.price(tx1["get_quantity"], tx1["give_quantity"])
        tx1_inverse_price = util.price(tx1["give_quantity"], tx1["get_quantity"])

        # Protocol change.
        if tx["block_index"] < 286000:
            tx1_inverse_price = util.price(1, tx1_price)

        logger.debug("Tx0 Price: {}; Tx1 Inverse Price: {}".format(float(tx0_price), float(tx1_inverse_price)))
        if tx0_price > tx1_inverse_price:
            logger.debug("Skipping: price mismatch.")
        else:
            logger.debug(
                "Potential forward quantities: {}, {}".format(
                    tx0_give_remaining, int(util.price(tx1_give_remaining, tx0_price))
                )
            )
            forward_quantity = int(min(tx0_give_remaining, int(util.price(tx1_give_remaining, tx0_price))))
            logger.debug("Forward Quantity: {}".format(forward_quantity))
            backward_quantity = round(forward_quantity * tx0_price)
            logger.debug("Backward Quantity: {}".format(backward_quantity))

            if not forward_quantity:
                logger.debug("Skipping: zero forward quantity.")
                continue
            if block_index >= 286500 or config.TESTNET:  # Protocol change.
                if not backward_quantity:
                    logger.debug("Skipping: zero backward quantity.")
                    continue

            forward_asset, backward_asset = tx1["get_asset"], tx1["give_asset"]

            if block_index >= 313900 or config.TESTNET:  # Protocol change.
                min_shell_quantity = 0.001 * config.UNIT  # 0.001 SCH
                if (forward_asset == config.SCH and forward_quantity <= min_shell_quantity) or (
                    backward_asset == config.SCH and backward_quantity <= min_shell_quantity
                ):
                    logger.debug("Skipping: below minimum {} quantity".format(config.SCH))
                    continue

            # Check and update fee remainings.
            fee = 0
            if (
                block_index >= 286500 or config.TESTNET
            ):  # Protocol change. Deduct fee_required from provided_remaining, etc., if possible (else don’t match).
                if tx1["get_asset"] == config.SCH:

                    if block_index >= 310500 or config.TESTNET:  # Protocol change.
                        fee = int(tx1["fee_required"] * util.price(backward_quantity, tx1["give_quantity"]))
                    else:
                        fee = int(tx1["fee_required_remaining"] * util.price(forward_quantity, tx1_get_remaining))

                    logger.debug(
                        "Tx0 fee provided remaining: {}; required fee: {}".format(
                            tx0_fee_provided_remaining / config.UNIT, fee / config.UNIT
                        )
                    )
                    if tx0_fee_provided_remaining < fee:
                        logger.debug("Skipping: tx0 fee provided remaining is too low.")
                        continue
                    else:
                        tx0_fee_provided_remaining -= fee
                        if block_index >= 287800 or config.TESTNET:  # Protocol change.
                            tx1_fee_required_remaining -= fee

                elif tx1["give_asset"] == config.SCH:

                    if block_index >= 310500 or config.TESTNET:  # Protocol change.
                        fee = int(tx0["fee_required"] * util.price(backward_quantity, tx0["give_quantity"]))
                    else:
                        fee = int(tx0["fee_required_remaining"] * util.price(backward_quantity, tx0_get_remaining))

                    logger.debug(
                        "Tx1 fee provided remaining: {}; required fee: {}".format(
                            tx1_fee_provided_remaining / config.UNIT, fee / config.UNIT
                        )
                    )
                    if tx1_fee_provided_remaining < fee:
                        logger.debug("Skipping: tx1 fee provided remaining is too low.")
                        continue
                    else:
                        tx1_fee_provided_remaining -= fee
                        if block_index >= 287800 or config.TESTNET:  # Protocol change.
                            tx0_fee_required_remaining -= fee

            else:  # Don’t deduct.
                if tx1["get_asset"] == config.SCH:
                    if tx0_fee_provided_remaining < tx1["fee_required"]:
                        continue
                elif tx1["give_asset"] == config.SCH:
                    if tx1_fee_provided_remaining < tx0["fee_required"]:
                        continue

            if config.SCH in (tx1["give_asset"], tx1["get_asset"]):
                status = "pending"
            else:
                status = "completed"
                # Credit.
                util.credit(
                    db, tx1["source"], tx1["get_asset"], forward_quantity, action="order match", event=order_match_id
                )
                util.credit(
                    db, tx0["source"], tx0["get_asset"], backward_quantity, action="order match", event=order_match_id
                )

            # Debit the order, even if it involves giving SatoshiChains, and so one
            # can't debit the sending account.
            # Get remainings may be negative.
            tx0_give_remaining -= forward_quantity
            tx0_get_remaining -= backward_quantity
            tx1_give_remaining -= backward_quantity
            tx1_get_remaining -= forward_quantity

            # Update give_remaining, get_remaining.
            # tx0
            tx0_status = "open"
            if tx0_give_remaining <= 0 or (
                tx0_get_remaining <= 0 and (block_index >= 292000 or config.TESTNET)
            ):  # Protocol change
                if tx0["give_asset"] != config.SCH and tx0["get_asset"] != config.SCH:
                    # Fill order, and recredit give_remaining.
                    tx0_status = "filled"
                    util.credit(
                        db, tx0["source"], tx0["give_asset"], tx0_give_remaining, event=tx1["tx_hash"], action="filled"
                    )
            bindings = {
                "give_remaining": tx0_give_remaining,
                "get_remaining": tx0_get_remaining,
                "fee_required_remaining": tx0_fee_required_remaining,
                "fee_provided_remaining": tx0_fee_provided_remaining,
                "status": tx0_status,
                "tx_hash": tx0["tx_hash"],
            }
            sql = "update orders set give_remaining = :give_remaining, get_remaining = :get_remaining, fee_required_remaining = :fee_required_remaining, fee_provided_remaining = :fee_provided_remaining, status = :status where tx_hash = :tx_hash"
            cursor.execute(sql, bindings)
            log.message(db, block_index, "update", "orders", bindings)
            # tx1
            if tx1_give_remaining <= 0 or (
                tx1_get_remaining <= 0 and (block_index >= 292000 or config.TESTNET)
            ):  # Protocol change
                if tx1["give_asset"] != config.SCH and tx1["get_asset"] != config.SCH:
                    # Fill order, and recredit give_remaining.
                    tx1_status = "filled"
                    util.credit(
                        db, tx1["source"], tx1["give_asset"], tx1_give_remaining, event=tx0["tx_hash"], action="filled"
                    )
            bindings = {
                "give_remaining": tx1_give_remaining,
                "get_remaining": tx1_get_remaining,
                "fee_required_remaining": tx1_fee_required_remaining,
                "fee_provided_remaining": tx1_fee_provided_remaining,
                "status": tx1_status,
                "tx_hash": tx1["tx_hash"],
            }
            sql = "update orders set give_remaining = :give_remaining, get_remaining = :get_remaining, fee_required_remaining = :fee_required_remaining, fee_provided_remaining = :fee_provided_remaining, status = :status where tx_hash = :tx_hash"
            cursor.execute(sql, bindings)
            log.message(db, block_index, "update", "orders", bindings)

            # Calculate when the match will expire.
            if block_index >= 308000 or config.TESTNET:  # Protocol change.
                match_expire_index = block_index + 20
            elif block_index >= 286500 or config.TESTNET:  # Protocol change.
                match_expire_index = block_index + 10
            else:
                match_expire_index = min(tx0["expire_index"], tx1["expire_index"])

            # Record order match.
            bindings = {
                "id": util.make_id(tx0["tx_hash"], tx["tx_hash"]),
                "tx0_index": tx0["tx_index"],
                "tx0_hash": tx0["tx_hash"],
                "tx0_address": tx0["source"],
                "tx1_index": tx1["tx_index"],
                "tx1_hash": tx1["tx_hash"],
                "tx1_address": tx1["source"],
                "forward_asset": forward_asset,
                "forward_quantity": forward_quantity,
                "backward_asset": backward_asset,
                "backward_quantity": backward_quantity,
                "tx0_block_index": tx0["block_index"],
                "tx1_block_index": tx1["block_index"],
                "block_index": block_index,
                "tx0_expiration": tx0["expiration"],
                "tx1_expiration": tx1["expiration"],
                "match_expire_index": match_expire_index,
                "fee_paid": fee,
                "status": status,
            }
            sql = "insert into order_matches values(:id, :tx0_index, :tx0_hash, :tx0_address, :tx1_index, :tx1_hash, :tx1_address, :forward_asset, :forward_quantity, :backward_asset, :backward_quantity, :tx0_block_index, :tx1_block_index, :block_index, :tx0_expiration, :tx1_expiration, :match_expire_index, :fee_paid, :status)"
            cursor.execute(sql, bindings)

            if tx1_status == "filled":
                break

    cursor.close()
    return
Esempio n. 12
0
def cancel_order_match(db, order_match, status, block_index):
    """The only cancelling is an expiration.
    """

    cursor = db.cursor()

    # Skip order matches just expired as a penalty. (Not very efficient.)
    if not (block_index >= 314250 or config.TESTNET):  # Protocol change.
        order_matches = list(
            cursor.execute(
                """SELECT * FROM order_matches \
                                               WHERE (id = ? AND status = ?)""",
                (order_match["id"], "expired"),
            )
        )
        if order_matches:
            cursor.close()
            return

    # Update status of order match.
    bindings = {"status": status, "order_match_id": order_match["id"]}
    sql = "update order_matches set status = :status where id = :order_match_id"
    cursor.execute(sql, bindings)
    log.message(db, block_index, "update", "order_matches", bindings)

    order_match_id = util.make_id(order_match["tx0_hash"], order_match["tx1_hash"])

    # If tx0 is dead, credit address directly; if not, replenish give remaining, get remaining, and fee required remaining.
    orders = list(
        cursor.execute(
            """SELECT * FROM orders \
                                    WHERE tx_index = ?""",
            (order_match["tx0_index"],),
        )
    )
    assert len(orders) == 1
    tx0_order = orders[0]
    if tx0_order["status"] in ("expired", "cancelled"):
        tx0_order_status = tx0_order["status"]
        if order_match["forward_asset"] != config.SCH:
            util.credit(
                db,
                order_match["tx0_address"],
                order_match["forward_asset"],
                order_match["forward_quantity"],
                action="order {}".format(tx0_order_status),
                event=order_match["id"],
            )
    else:
        tx0_give_remaining = tx0_order["give_remaining"] + order_match["forward_quantity"]
        tx0_get_remaining = tx0_order["get_remaining"] + order_match["backward_quantity"]
        if tx0_order["get_asset"] == config.SCH and (block_index >= 297000 or config.TESTNET):  # Protocol change.
            tx0_fee_required_remaining = tx0_order["fee_required_remaining"] + order_match["fee_paid"]
        else:
            tx0_fee_required_remaining = tx0_order["fee_required_remaining"]
        tx0_order_status = tx0_order["status"]
        bindings = {
            "give_remaining": tx0_give_remaining,
            "get_remaining": tx0_get_remaining,
            "status": tx0_order_status,
            "fee_required_remaining": tx0_fee_required_remaining,
            "tx_hash": order_match["tx0_hash"],
        }
        sql = "update orders set give_remaining = :give_remaining, get_remaining = :get_remaining, fee_required_remaining = :fee_required_remaining where tx_hash = :tx_hash"
        cursor.execute(sql, bindings)
        log.message(db, block_index, "update", "orders", bindings)

    # If tx1 is dead, credit address directly; if not, replenish give remaining, get remaining, and fee required remaining.
    orders = list(
        cursor.execute(
            """SELECT * FROM orders \
                                    WHERE tx_index = ?""",
            (order_match["tx1_index"],),
        )
    )
    assert len(orders) == 1
    tx1_order = orders[0]
    if tx1_order["status"] in ("expired", "cancelled"):
        tx1_order_status = tx1_order["status"]
        if order_match["backward_asset"] != config.SCH:
            util.credit(
                db,
                order_match["tx1_address"],
                order_match["backward_asset"],
                order_match["backward_quantity"],
                action="order {}".format(tx1_order_status),
                event=order_match["id"],
            )
    else:
        tx1_give_remaining = tx1_order["give_remaining"] + order_match["backward_quantity"]
        tx1_get_remaining = tx1_order["get_remaining"] + order_match["forward_quantity"]
        if tx1_order["get_asset"] == config.SCH and (block_index >= 297000 or config.TESTNET):  # Protocol change.
            tx1_fee_required_remaining = tx1_order["fee_required_remaining"] + order_match["fee_paid"]
        else:
            tx1_fee_required_remaining = tx1_order["fee_required_remaining"]
        tx1_order_status = tx1_order["status"]
        bindings = {
            "give_remaining": tx1_give_remaining,
            "get_remaining": tx1_get_remaining,
            "status": tx1_order_status,
            "fee_required_remaining": tx1_fee_required_remaining,
            "tx_hash": order_match["tx1_hash"],
        }
        sql = "update orders set give_remaining = :give_remaining, get_remaining = :get_remaining, fee_required_remaining = :fee_required_remaining where tx_hash = :tx_hash"
        cursor.execute(sql, bindings)
        log.message(db, block_index, "update", "orders", bindings)

    if block_index < 286500:  # Protocol change.
        # Sanity check: one of the two must have expired.
        tx0_order_time_left = tx0_order["expire_index"] - block_index
        tx1_order_time_left = tx1_order["expire_index"] - block_index
        assert tx0_order_time_left or tx1_order_time_left

    # Penalize tardiness.
    if block_index >= 313900 or config.TESTNET:  # Protocol change.
        if tx0_order["status"] == "expired" and order_match["forward_asset"] == config.SCH:
            exact_penalty(db, order_match["tx0_address"], block_index, order_match["id"])
        if tx1_order["status"] == "expired" and order_match["backward_asset"] == config.SCH:
            exact_penalty(db, order_match["tx1_address"], block_index, order_match["id"])

    # Re‐match.
    if block_index >= 310000 or config.TESTNET:  # Protocol change.
        if not (block_index >= 315000 or config.TESTNET):  # Protocol change.
            cursor.execute(
                """SELECT * FROM transactions\
                              WHERE tx_hash = ?""",
                (tx0_order["tx_hash"],),
            )
            match(db, list(cursor)[0], block_index)
            cursor.execute(
                """SELECT * FROM transactions\
                              WHERE tx_hash = ?""",
                (tx1_order["tx_hash"],),
            )
            match(db, list(cursor)[0], block_index)

    if status == "expired":
        # Record order match expiration.
        bindings = {
            "order_match_id": order_match["id"],
            "tx0_address": order_match["tx0_address"],
            "tx1_address": order_match["tx1_address"],
            "block_index": block_index,
        }
        sql = "insert into order_match_expirations values(:order_match_id, :tx0_address, :tx1_address, :block_index)"
        cursor.execute(sql, bindings)

    cursor.close()
Esempio n. 13
0
def parse (db, tx, MAINNET_BURNS, message=None):
    burn_parse_cursor = db.cursor()

    if config.TESTNET:
        status = 'valid'

        if status == 'valid':
            problems = validate(db, tx['source'], tx['destination'], tx['shell_amount'], tx['block_index'], overburn=False)
            if problems: status = 'invalid: ' + '; '.join(problems)

            if tx['shell_amount'] != None:
                sent = tx['shell_amount']
            else:
                sent = 0

        if status == 'valid':
            # Calculate quantity of SHP earned. (Maximum 1 SCH in total, ever.)
            cursor = db.cursor()
            cursor.execute('''SELECT * FROM burns WHERE (status = ? AND source = ?)''', ('valid', tx['source']))
            burns = cursor.fetchall()
            already_burned = sum([burn['burned'] for burn in burns])
            ONE = 1 * config.UNIT
            max_burn = ONE - already_burned
            if sent > max_burn: burned = max_burn   # Exceeded maximum burn; earn what you can.
            else: burned = sent

            total_time = config.BURN_END - config.BURN_START
            partial_time = config.BURN_END - tx['block_index']
            multiplier = (1000 + (500 * Fraction(partial_time, total_time)))
            earned = round(burned * multiplier)

            # Credit source address with earned SHP.
            util.credit(db, tx['source'], config.SHP, earned, action='burn', event=tx['tx_hash'])
        else:
            burned = 0
            earned = 0

        tx_index = tx['tx_index']
        tx_hash = tx['tx_hash']
        block_index = tx['block_index']
        source = tx['source']

    else:
        # Mainnet burns are hard‐coded.

        try:
            line = MAINNET_BURNS[tx['tx_hash']]
        except KeyError:
            return

        util.credit(db, line['source'], config.SHP, int(line['earned']), action='burn', event=line['tx_hash'])

        tx_index = tx['tx_index']
        tx_hash = line['tx_hash']
        block_index = line['block_index']
        source = line['source']
        burned = line['burned']
        earned = line['earned']
        status = 'valid'

    # Add parsed transaction to message-type–specific table.
    # TODO: store sent in table
    bindings = {
        'tx_index': tx_index,
        'tx_hash': tx_hash,
        'block_index': block_index,
        'source': source,
        'burned': burned,
        'earned': earned,
        'status': status,
    }
    sql='insert into burns values(:tx_index, :tx_hash, :block_index, :source, :burned, :earned, :status)'
    burn_parse_cursor.execute(sql, bindings)

    burn_parse_cursor.close()
Esempio n. 14
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