示例#1
0
文件: api.py 项目: waosman/grin-pool
 def post(self, id, field, value):
     global database
     #database = lib.get_db()
     LOGGER = lib.get_logger(PROCESS)
     # AUTH FILTER
     if id != g.user.id:
         response = jsonify(
             {'message': 'Not authorized to access data for other users'})
         response.status_code = 403
         return response
     debug and LOGGER.warn(
         "WorkerAPI_utxo post id:{} field:{} value:{}".format(
             id, field, value))
     allowed_fields = ["address", "method"]
     if field not in allowed_fields:
         response = jsonify({'message': 'Invalid field for update'})
         response.status_code = 403
         return response
     st = False
     if field == "address":
         st = Pool_utxo.update_address(id, value)
     elif field == "method":
         st = Pool_utxo.update_method(id, value)
     if st == False:
         response = jsonify(
             {'message': 'Failed to update {}'.format(field)})
         response.status_code = 500
         return response
     else:
         response = jsonify({field: value})
         response.status_code = 200
         return response
示例#2
0
def submit_tx_slate(user_id, slate, logger, database):
    if slate is None:
        message = "No slate data provided"
        logger.warn(message)
        raise PaymentError(400, message)
    try:
        slate_json = json.loads(slate.decode('utf-8'))
        tx_id = slate_json["id"]
    except Exception as e:
        message = "Invalid slate data provided"
        logger.warn(message)
        raise PaymentError(400, message)

    try:
        logger.warn("Running submit_slate: tx_id = {}".format(tx_id))
        # Record Keeping
        timestamp = datetime.utcnow()
        locked_utxo = Pool_utxo.get_locked_by_userid(user_id)
        locked_utxo.last_success = timestamp
        locked_utxo.failure_count = 0
        payment_rec = Pool_payment.get_by_address(tx_id)
        payment_rec.state = "posted"
        finalized_slate = wallet.finalize_tx(slate_json)
        payment_rec.tx_data = json.dumps(finalized_slate)
        database.db.getSession().commit()
    except Exception as e:
        logger.exception("Unexpected Error in submit_tx_slate")
        raise PaymentError(500, str(e))

    # Post the TX
    try:
        wallet.post_tx(finalized_slate["tx"])
    except Exception as e:
        logger.exception("Failed to post payment: {}".format(repr(e)))
        raise PaymentError(500, str(e))
示例#3
0
 def get(self, id=None, fields=None):
     database = lib.get_db()
     LOGGER = lib.get_logger(PROCESS)
     LOGGER.warn("WorkerAPI_payments get id:{} fields:{}".format(
         id, fields))
     fields = lib.fields_to_list(fields)
     utxo = Pool_utxo.get_by_address(id)
     return utxo.to_json(fields)
示例#4
0
def main():
    global LOGGER
    global CONFIG
    CONFIG = lib.get_config()
    LOGGER = lib.get_logger(PROCESS)
    LOGGER.warn("=== Starting {}".format(PROCESS))

    # Get Config settings
    pool_fee = float(CONFIG[PROCESS]["pool_fee"])
    # Number of blocks of share data used to calculate rewards
    PPLNG_WINDOW_SIZE = 60
    try:
        PPLNG_WINDOW_SIZE = int(os.environ["PPLNG_WINDOW_SIZE"])
    except Exception as e:
        LOGGER.error(
            "Failed to get PPLNG_WINDOW_SIZE from the environment: {}  Using default size of {}"
            .format(e, PPLNG_WINDOW_SIZE))

    # Connect to DB
    database = lib.get_db()

    # Get current blockchain height
    chain_height = grin.blocking_get_current_height()

    # Get unlocked blocks from the db
    unlocked_blocks = Pool_blocks.get_all_unlocked()
    unlocked_blocks = [blk.height for blk in unlocked_blocks]
    LOGGER.warn("Paying for {} pool blocks: {}".format(len(unlocked_blocks),
                                                       unlocked_blocks))
    for height in unlocked_blocks:
        try:
            LOGGER.warn("Processing unlocked block: {}".format(height))
            # Call the library routine to get this blocks payout map
            payout_map = pool.calculate_block_payout_map(
                height, PPLNG_WINDOW_SIZE, pool_fee, LOGGER, False)
            #print("payout_map = {}".format(payout_map))
            # Store the payment map for this block
            credits_record = Pool_credits(chain_height, height, payout_map)
            database.db.getSession().add(credits_record)
            # Make payments based on the workers total share_value
            Pool_blocks.setState(height, "paid")
            for user_id, payment_amount in payout_map.items():
                # Add worker rewards to pool account balance
                LOGGER.warn("Credit to user: {} = {}".format(
                    user_id, payment_amount))
                worker_utxo = Pool_utxo.credit_worker(user_id, payment_amount)
                # Worker_stats accounting and running totals
                #latest_worker_stats = Worker_stats.get_latest_by_id(user_id)
                #latest_worker_stats.dirty = True
            database.db.getSession().commit()

        except Exception as e:
            database.db.getSession().rollback()
            LOGGER.exception("Something went wrong: {}".format(repr(e)))

    LOGGER.warn("=== Completed {}".format(PROCESS))
    sys.stdout.flush()
示例#5
0
def main():
    global LOGGER
    LOGGER = lib.get_logger(PROCESS)
    LOGGER.warn("=== Starting {}".format(PROCESS))

    # Connect to DB
    database = lib.get_db()

    latest_block = 0

    # XXX All in one db transaction....
    # Get unlocked blocks from the db
    unlocked_blocks = Pool_blocks.get_all_unlocked()
    database.db.getSession().commit()
    for pb in unlocked_blocks:
        try:
            LOGGER.warn("Processing unlocked block: {}".format(pb))
            if pb.height > latest_block:
                latest_block = pb.height
            # Get valid pool_shares for that block from the db
            pool_shares = Pool_shares.get_valid_by_height(pb.height)
            # Calculate Payment info:
            worker_shares = {}
            for ps in pool_shares:
                LOGGER.warn("Processing pool_shares: {}".format(ps))
                # Need to get actual_difficulty
                gs = Grin_shares.get_by_nonce(ps.nonce)
                if gs == None:
                    # XXX NOTE: no payout for shares not accepted by grin node
                    continue
                if ps.found_by in worker_shares:
                    worker_shares[ps.found_by] += gs.actual_difficulty
                else:
                    worker_shares[ps.found_by] = gs.actual_difficulty
            if len(worker_shares) > 0:
                # Calcualte reward/difficulty: XXX TODO: Enhance
                #  What algorithm to use?  Maybe: https://slushpool.com/help/manual/rewards
                r_per_d = REWARD / sum(worker_shares.values())
                for worker in worker_shares.keys():
                    # Calculate reward per share
                    worker_rewards = worker_shares[worker] * r_per_d
                    # Add or create worker rewards
                    worker_utxo = Pool_utxo.credit_worker(
                        worker, worker_rewards)
                    LOGGER.warn("Credit to user: {} = {}".format(
                        worker, worker_rewards))
            # Mark the pool_block state="paid" (maybe "processed" would be more accurate?)
            pb.state = "paid"
            database.db.getSession().commit()
        except Exception as e:
            database.db.getSession().rollback()
            LOGGER.error("Something went wrong: {}".format(e))

    #database.db.getSession().commit()
    # db.set_last_run(PROCESS, str(time.time()))
    LOGGER.warn("=== Completed {}".format(PROCESS))
    sys.stdout.flush()
示例#6
0
 def create(cls, username, password):
     if username is None or password is None:
         return None
     username = username.lower()
     try:
         user_rec = Users(username, password)
         database.db.createDataObj(user_rec)
         # Create the users utxo record
         pool_utxo = Pool_utxo(user_rec.id)
         database.db.createDataObj(pool_utxo)
         return user_rec
     except:
         # XXX TODO: Log err
         return None
示例#7
0
文件: api.py 项目: bitgrin/grin-pool
 def get(self, id, fields=None):
     global database
     #database = lib.get_db()
     LOGGER = lib.get_logger(PROCESS)
     # AUTH FILTER
     if id != g.user.id:
         response = jsonify({ 'message': 'Not authorized to access data for other users' })
         response.status_code = 403
         return response
     debug and LOGGER.warn("WorkerAPI_utxo get id:{} fields:{}".format(id, fields))
     fields = lib.fields_to_list(fields)
     utxo = Pool_utxo.get_by_userid(id)
     if utxo is None:
         return None
     return utxo.to_json(fields)
示例#8
0
def main():
    global LOGGER
    global CONFIG
    CONFIG = lib.get_config()
    LOGGER = lib.get_logger(PROCESS)
    LOGGER.warn("=== Starting {}".format(PROCESS))

    # Connect to DB
    database = lib.get_db()
    # Configs
    minimum_payout = int(CONFIG[PROCESS]["minimum_payout"])
    walletauth = (wallet_api_user, wallet_api_key)

    utxos = Pool_utxo.getPayable(minimum_payout)

    # XXX TODO: Use the current balance, timestamp, the last_attempt timestamp, last_payout, and failed_attempts
    # XXX TODO: to filter and sort by order we want to make payment attempts
    for utxo in utxos:
        try:
            # Try less often for wallets that dont answer
            if utxo.amount < utxo.failure_count:
                if randint(0, 11) != 0:
                    continue
            LOGGER.warn(
                "Processing utxo for: {} {} {} using method: {}".format(
                    utxo.user_id, utxo.address, utxo.amount, utxo.method))
            if utxo.method in ["http", "https", "keybase"]:
                try:
                    #user_id,      address,      logger, database, wallet_auth, method,     invoked_by
                    payments.atomic_send(utxo.user_id, utxo.address, LOGGER,
                                         database, walletauth, utxo.method,
                                         "schedule")
                except payments.PaymentError as e:
                    LOGGER.error("Failed to make http payment: {}".format(e))
            else:
                LOGGER.warn(
                    "Automatic payment does not (yet?) support method: {}".
                    format(utxo.method))

        except Exception as e:
            LOGGER.error("Failed to process utxo: {} because {}".format(
                utxo.user_id, str(e)))
            database.db.getSession().rollback()
            sys.exit(1)

    LOGGER.warn("=== Completed {}".format(PROCESS))
示例#9
0
def main():
    global LOGGER
    LOGGER = lib.get_logger(PROCESS)
    LOGGER.warn("=== Starting {}".format(PROCESS))

    # Connect to DB
    database = lib.get_db()

    latest_block = 0

    # XXX All in one db transaction....
    # Get unlocked blocks from the db
    unlocked_blocks = Pool_blocks.get_all_unlocked()
    database.db.getSession().commit()
    for pb in unlocked_blocks:
        try:
            LOGGER.warn("Processing unlocked block: {}".format(pb))
            if pb.height > latest_block:
                latest_block = pb.height
            # Get Worker_stats of this block to calculate reward for each worker
            worker_stats = Worker_stats.get_by_height(pb.height)
            # Calculate Payment info:
            if len(worker_stats) > 0:
                # Calcualte reward/share:
                # XXX TODO: Enhance
                #  What algorithm to use?  Maybe: https://slushpool.com/help/manual/rewards
                r_per_g = REWARD / sum([st.gps for st in worker_stats])
                for stat in worker_stats:
                    # Calculate reward
                    worker_rewards = stat.gps * r_per_g
                    # Add or create worker rewards
                    worker_utxo = Pool_utxo.credit_worker(
                        stat.worker, worker_rewards)
                    LOGGER.warn("Credit to user: {} = {}".format(
                        stat.worker, worker_rewards))
            # Mark the pool_block state="paid" (maybe "processed" would be more accurate?)
            pb.state = "paid"
            database.db.getSession().commit()
        except Exception as e:
            database.db.getSession().rollback()
            LOGGER.error("Something went wrong: {}".format(e))

    #database.db.getSession().commit()
    LOGGER.warn("=== Completed {}".format(PROCESS))
    sys.stdout.flush()
示例#10
0
def main():
    global LOGGER
    LOGGER = lib.get_logger(PROCESS)
    LOGGER.warn("=== Starting {}".format(PROCESS))

    # Connect to DB
    database = lib.get_db()

    # XXX All in one db transaction....
    # Get unlocked blocks from the db
    unlocked_blocks = Pool_blocks.get_all_unlocked()
    unlocked_blocks = [blk.height for blk in unlocked_blocks]
    for height in unlocked_blocks:
        try:
            LOGGER.warn("Processing unlocked block: {}".format(height))
            # Call the library routine to get this blocks payout map
            payout_map = pool.calculate_block_payout_map(
                height, PPLNS_WINDOW, LOGGER, False)
            #print("payout_map = {}".format(payout_map))
            # Make payments based on the workers total share_value
            Pool_blocks.setState(height, "paid")
            database.db.getSession().commit()
            for user_id, payment_amount in payout_map.items():
                # Add worker rewards to pool account balance
                LOGGER.warn("Credit to user: {} = {}".format(
                    user_id, payment_amount))
                worker_utxo = Pool_utxo.credit_worker(user_id, payment_amount)
                # Worker_stats accounting and running totals
                #latest_worker_stats = Worker_stats.get_latest_by_id(user_id)
                #latest_worker_stats.dirty = True
            database.db.getSession().commit()

        except Exception as e:
            database.db.getSession().rollback()
            LOGGER.error("Something went wrong: {} - {}".format(
                e, traceback.print_exc()))

    LOGGER.warn("=== Completed {}".format(PROCESS))
    sys.stdout.flush()
示例#11
0
def cancel_tx_slate(tx_slate_id, new_state, logger, database):
    try:
        logger.warn("In cancel_tx_slate")
        # For tx sent via slate, tx_id is in the pool_payment.address field
        payment_rec = Pool_payment.get_by_address(tx_slate_id)
        if payment_rec is None:
            message = "Could not find any payment record for tx_slate_id {}".format(
                tx_slate_id)
            logger.warn(message)
            raise PaymentError(400, message)
        # Check if the wallet already has this marked as canceled
        wallet_rec = wallet.retrieve_txs(tx_slate_id=tx_slate_id)
        if len(wallet_rec) == 0:
            logger.warn(
                "Wallet has no record of tx_slate_id: {}".format(tx_slate_id))
        else:
            logger.warn("XXX: wallet_rec = {}".format(wallet_rec))
            assert wallet_rec[0][
                "tx_slate_id"] == tx_slate_id, "Wallet returned incorrect tx data: {} vs {}".format(
                    wallet_rec[0]["tx_slate_id"], tx_slate_id)
            if wallet_rec[0]["tx_type"] == "TxSentCancelled":
                logger.warn(
                    "Tx already marked canceled: {}".format(tx_slate_id))
            else:
                wallet.cancel_tx(tx_slate_id=tx_slate_id)
        # Mark payment record state as expired or canceled
        payment_rec.state = new_state
        # Credit back the user utxo amount
        locked_utxo = Pool_utxo.get_locked_by_userid(payment_rec.user_id)
        locked_utxo.amount = locked_utxo.amount + payment_rec.amount + payment_rec.fee
        locked_utxo.failure_count += payment_rec.failure_count + 1
        database.db.getSession().commit()
    except Exception as e:
        logger.exception("Unexpected Error in cancel_tx_slate: {}".format(
            str(e)))
        raise PaymentError(500, str(e))
示例#12
0
文件: api.py 项目: bitgrin/grin-pool
 def post(self, id, function, address=None):
     global database
     LOGGER = lib.get_logger(PROCESS)
     debug = True
     debug and LOGGER.warn("PoolAPI_paymentrequest POST: {} - {}".format(id, function))
     # AUTH FILTER
     if id != g.user.id:
         response = jsonify({ 'message': 'Not authorized to access data for other users' })
         response.status_code = 403
         return response
     # XXX TODO: Get from config
     payment_req_url = "http://grinwallet:13425"
     # Get the users balance then call the internal payment request api to
     # generate a payment tx slate.  Return that slate to the caller
     if function == "get_tx_slate":
         # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
         #LOGGER.warn("SLATE payment requests are disabled")
         #response = jsonify({ 'message': 'File-Based payouts are temporarily disabled' })
         #response.status_code = 400
         #return response
         # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
         ##
         # Offline Phase 1) issue_send_tx
         # Generate the send transaction slate
         utxo = Pool_utxo.get_by_userid(id)
         amount = utxo.amount
         user_id = str(utxo.user_id)
         # XXX TODO: Check if greater than minimum payout
         get_tx_slate_url = payment_req_url + "/pool/payment/get_tx_slate/"+user_id
         debug and LOGGER.warn("Requesting Payment slate: {}".format(get_tx_slate_url))
         r = requests.post(
                 url=get_tx_slate_url,
                 auth=(admin_user, admin_pass)
             )
         debug and LOGGER.warn("get_tx_slate call: {} - {}".format(r.status_code, r.reason))
         if r.status_code != 200:
             LOGGER.warn("Failed to get a payment slate: {} - {} - {}".format(r.status_code, r.reason, r.json()["message"]))
             response = jsonify({ 'message': 'Failed to get a payment slate: {}'.format(r.json()["message"])})
             response.status_code = 400
             return response
         return(json.loads(r.text))
     elif function == "submit_tx_slate":
         ##
         # Offline Phase 2) finalize_tx
         # Submit the signed slate to be finalized
         debug and LOGGER.warn("submit_slate: {}".format(id))
         try:
             requestdata = request.data
             rdjson = json.loads(requestdata.decode('utf-8'))
             debug and LOGGER.warn("PoolAPI_paymentrequest POST: requestdata:{}".format(rdjson))
         except AttributeError as e:
             LOGGER.warn("Missing tx_slate data - {}".format(request.data))
             response = jsonify({ 'message': 'Missing signed slate data' })
             response.status_code = 400
             return response
         except json.decoder.JSONDecodeError:
             LOGGER.warn("Invalid tx_slate data - {}".format(request.data))
             response = jsonify({ 'message': 'Invalid signed slate data was submitted' })
             response.status_code = 400
             return response
         debug and LOGGER.warn("submit_slate: {}".format(requestdata))
         submit_tx_slate_url = payment_req_url + "/pool/payment/submit_tx_slate/"+str(id)
         r = requests.post(
                 url=submit_tx_slate_url, 
                 data=requestdata,
                 auth=(admin_user, admin_pass)
             )
         if r.status_code != 200:
             #LOGGER.warn("Failed to submit payment slate: {} - {} - {}".format(r.status_code, r.reason, r.json()["message"]))
             #response = jsonify({ 'message': 'Failed to submit payment slate: {}'.format(r.json()["message"]) })
             LOGGER.warn("Failed to submit payment slate: {} - {}".format(r.status_code, r.reason))
             response = jsonify({ 'message': 'Failed to submit payment slate: {}'.format(r.reason) })
             response.status_code = 500
             return response
         debug and LOGGER.warn("submit_tx_slate result: {} - {}".format(r.status_code, r.text))
         return "ok"
     elif function == "http":
         ##
         # Online Wallet-To-Wallet
         debug and LOGGER.warn("Send HTTP transaction: {}".format(id))
         LOGGER.warn("request.args: {}".format(request.args))
         if address is None:
             LOGGER.warn("HTTP payment request missing address")
             response = jsonify({ 'message': 'Error, must specify a wallet address:port' })
             response.status_code = 400
             return response
         debug and LOGGER.warn("Initiate HTTP payment: {} - {}  - {}".format(id, address, request.args))
         LOGGER.warn("Args in json: {}".format(json.dumps(request.args)))
         http_payment_url = payment_req_url + "/pool/payment/http/{}/{}".format(id, address)
         r = requests.post(
                 url=http_payment_url,
                 data=json.dumps(request.args),
                 headers={'content-type': 'application/json'},
                 auth=(admin_user, admin_pass)
             )
         if r.status_code != 200:
             LOGGER.warn("Failed to complete HTTP payment: {} - {}".format(r.status_code, r.reason))
             response = jsonify({ 'message': 'Failed to complete HTTP payment: {}'.format(r.json()["message"]) })
             response.status_code = 400
             return response
         debug and LOGGER.warn("http payment result: {} - {}".format(r.status_code, r.text))
         return "ok"
     elif function == "keybase":
         ##
         # Online Wallet-To-Keybase-To-Keybase-To-Wallet
         debug and LOGGER.warn("Send keybase transaction: {}".format(id))
         if address is None:
             LOGGER.warn("keybase payment request missing address")
             response = jsonify({ 'message': 'Error, must specify a keybase username to send to' })
             response.status_code = 400
             return response
         debug and LOGGER.warn("Initiate keybase payment: {} - {}".format(id, address))
         keybase_payment_url = payment_req_url + "/pool/payment/keybase/{}/{}".format(id, address)
         r = requests.post(
                 url=keybase_payment_url, 
                 auth=(admin_user, admin_pass)
             )
         if r.status_code != 200:
             LOGGER.warn("Failed to complete keybase payment: {} - {}".format(r.status_code, r.reason))
             response = jsonify({ 'message': 'Failed to complete keybase payment: {}'.format(r.json()["message"]) })
             response.status_code = 400
             return response
         debug and LOGGER.warn("keybase payment result: {} - {}".format(r.status_code, r.text))
         return "ok"
     elif function == "payout_script":
         ##
         # Not really a payout request, rather, a request for payout automation script code
         debug and LOGGER.warn("Get Payout Script: {}".format(id))
         #file = open("/content/BGP_payout.py", "r")
         #payout_script = file.read() 
         #return payout_script
         return send_from_directory('/content', 'BGP_payout.py')
     else:
         LOGGER.warn("Invalid Payment Type requested")
         response = jsonify({ 'message': 'Error, must specify valid payment request method.  Method {} is not valid.'.format(function) })
         response.status_code = 400
         return response
示例#13
0
    print("Invalid state choice")
    sys.exit(1)

# Get the payout record
payout_record = Pool_payment.get_by_id(payout_id)
print("{}".format(payout_record))
amount = payout_record.amount / float(NANOGRIN)

# Get the user id
user_id = payout_record.user_id
user_record = Users.get_by_id(user_id)
username = user_record.username
print("User: {}".format(username))

# Get the users UTXO record
user_utxo = Pool_utxo.get_by_userid(user_id)

# Print a report
print("Will update account for {} on {}".format(username, poolname))
print(
    "Will cancel payout {} and add {} to users current balance of {} for a new balance of {}"
    .format(payout_id, amount,
            float(user_utxo.amount) / NANOGRIN,
            float(user_utxo.amount) / NANOGRIN + amount))

# Confirm action
print("")
proceed_or_exit()

# Do it
payout_record.state = new_state
示例#14
0
 def get(self, id=None, fields=None):
     database = lib.get_db()
     fields = lib.fields_to_list(fields)
     utxo = Pool_utxo.get_by_address(id)
     return utxo.to_json(fields)
示例#15
0
debit_pool = "x"
while debit_pool != "n" and debit_pool != "y":
    debit_pool = input("Debit the pools account? (y/n): ")
    if debit_pool != "n" and debit_pool != "y":
        print("Invalid input")

# Get the user id
user_id = Users.get_id_by_username(username)
if user_id == 0:
    print("Could not find user: {} in the database".format(username))
    sys.exit(1)
#user_record = Users.get_by_id(user_id)
#print(user_record)

# Get the users UTXO record
user_utxo = Pool_utxo.get_by_userid(user_id)
# Get the pools UTXO if needed
if debit_pool == "y":
    pooladmin_utxo = Pool_utxo.get_by_userid(1)

# Print a report
print("Will update account for {} on {}".format(username, poolname))
print("Will add {} to users current balance of {} for a new balance of {}".
      format(amount,
             float(user_utxo.amount) / NANOGRIN,
             float(user_utxo.amount) / NANOGRIN + amount))
if debit_pool == "y":
    print(
        "Will subtract {} from pool admin current balance of {} for a new balance of {}"
        .format(amount,
                float(pooladmin_utxo.amount) / NANOGRIN,
示例#16
0
def main():
    global LOGGER
    global CONFIG
    CONFIG = lib.get_config()
    LOGGER = lib.get_logger(PROCESS)
    LOGGER.warn("=== Starting {}".format(PROCESS))

    # DB connection details
    db_host = CONFIG["db"]["address"] + ":" + CONFIG["db"]["port"]
    db_user = CONFIG["db"]["user"]
    db_password = CONFIG["db"]["password"]
    db_name = CONFIG["db"]["db_name"]
    mysqlcontsraints = MysqlConstants(db_host, db_user, db_password, db_name)

    # Connect to DB
    database.db = database_details(MYSQL_CONSTANTS=mysqlcontsraints)
    database.db.initialize()

    wallet_dir = CONFIG[PROCESS]["wallet_dir"]
    minimum_payout = int(CONFIG[PROCESS]["minimum_payout"])
    os.chdir(wallet_dir)
    utxos = Pool_utxo.getPayable(minimum_payout)
    database.db.getSession().commit()
    # XXX TODO: Use the current balance, timestamp, the last_attempt timestamp, last_payout, and failed_attempts
    # XXX TODO: to filter and sort by order we want to make payment attempts
    for utxo in utxos:
        try:
            LOGGER.warn("Trying to pay: {} {} {}".format(
                utxo.id, utxo.address, utxo.amount))
            # Lock just this current record for update
            locked_utxo = Pool_utxo.get_locked_by_id(utxo.id)
            # Save and Zero the balance
            original_balance = locked_utxo.amount
            locked_utxo.amount = 0
            # Savepoint changes - if we crash after sending coins but before commit we roll back to here.
            #   The pool audit service finds lost payouts and restores user balance
            database.db.getSession().begin_nested()
            # Attempt to make the payment
            timestamp = "{:%B %d, %Y %H:%M:%S.%f}".format(datetime.now())
            status = makePayout(locked_utxo.address, original_balance)
            LOGGER.warn("Payout status: {}".format(status))
            if status == 0:
                LOGGER.warn("Made payout for {} {} {}".format(
                    locked_utxo.id, locked_utxo.address, original_balance))
                # Update timestamp of last payout, number of failed payout attempts
                locked_utxo.amount = 0
                locked_utxo.failure_count = 0
                locked_utxo.last_try = timestamp
                locked_utxo.last_success = timestamp
                # Commit changes
                database.db.getSession().commit()
            else:
                LOGGER.error("Failed to make payout: {} {} {}".format(
                    locked_utxo.id, locked_utxo.address, original_balance))
                # Restore the users balance
                locked_utxo.amount = original_balance
                # Update number of failed payout attempts
                if locked_utxo.failure_count is None:
                    locked_utxo.failure_count = 0
                locked_utxo.failure_count += 1
                locked_utxo.last_try = timestamp
                # Commit changes
                database.db.getSession().commit()
            database.db.getSession().commit()

        except Exception as e:
            LOGGER.error("Failed to process utxo: {} because {}".format(
                utxo.id, str(e)))
            database.db.getSession().rollback()
            sys.exit(1)

    LOGGER.warn("=== Completed {}".format(PROCESS))
示例#17
0
def atomic_send(user_id, address, logger, database, method, invoked_by):
    # validate method
    if method not in ["http", "https", "keybase"]:
        message = "Invalid payment method requested"
        logger.warn(message)
        raise PaymentError(400, message)
    # Validate Address
    address = address.lstrip().rstrip()
    if address is None:
        message = "Wallet address is missing"
        logger.warn(message)
        raise PaymentError(400, message)
    if method == "http" or method == "https":
        if not address.startswith("http"):
            address = method + "://" + address
    valid = validateAddress(address, method, logger)
    if valid == False:
        message = "Wallet address is invalid: {}".format(address)
        logger.warn(message)
        raise PaymentError(400, message)
    if method == "http" or method == "https":
        probe = testWalletPort(address, logger)
        if probe == False:
            message = "Failed to establish connection with remote wallet listener at: {}".format(
                address)
            logger.warn(message)
            raise PaymentError(400, message)

    # Lock this utxo record for update and check for minimum balance
    amount = 0
    try:
        locked_utxo = Pool_utxo.get_locked_by_userid(user_id)
        if locked_utxo is None or locked_utxo.amount < (1 * 1000000000):
            message = "Insufficient available balance for payout"
            logger.warn(message)
            raise PaymentError(400, message)
        # Save the users current balance
        amount = locked_utxo.amount
    except PaymentError as e:  # My own errors
        raise
    except Exception as e:
        logger.exception("Failed to get worker balance: {}".format(str(e)))
        raise PaymentError(500, str(e))

    # Call the synchronous send method
    # Subtract the balance from UTXO
    slate = None
    try:
        timestamp = datetime.utcnow()
        # Send the TX
        if method == "http" or method == "https":
            slate = http_send(user_id, address, amount, logger)
        elif method == "keybase":
            slate = keybase_send(user_id, address, amount, logger)
        # Create a payment record
        payment_rec = Pool_payment(
            user_id=locked_utxo.user_id,
            timestamp=timestamp,
            height=slate["height"],
            address=str(address),
            amount=locked_utxo.amount,
            method=method,
            fee=int(slate["fee"]),
            failure_count=locked_utxo.failure_count,
            state="posted",
            tx_data=json.dumps(slate),
            invoked_by=invoked_by,
        )
        database.db.getSession().add(payment_rec)
        # Update the users utxo record
        locked_utxo.amount = int(slate["fee"]) * -1
        locked_utxo.last_try = timestamp
        locked_utxo.last_success = timestamp
        locked_utxo.total_amount += amount
        # Commit this
        database.db.getSession().commit()
    except PaymentError as e:  # My own errors
        logger.exception("Failed to send tx".format(repr(e)))
        if slate is not None:
            wallet.cancel_tx(tx_slate_id=slate["id"])
        raise e
    except Exception as e:  # All other (unexpected) errors
        logger.exception("Failed to create payment because {}".format(repr(e)))
        raise PaymentError(500, str(e))

    # Post the TX
    try:
        logger.warn("Debug: Post the TX")
        wallet.post_tx(slate["tx"])
    except Exception as e:  # All other (unexpected) errors
        # Tidy will handle the refund to utxo
        # Tidy will mark payment record expired
        # Tidy will call cancel_tx_slate to unlock the wallet outputs
        logger.exception("Failed to post payment because {}".format(repr(e)))
        raise PaymentError(500, str(e))
示例#18
0
def main():
    global LOGGER
    global CONFIG
    CONFIG = lib.get_config()
    LOGGER = lib.get_logger(PROCESS)
    LOGGER.warn("=== Starting {}".format(PROCESS))

    # Connect to DB
    try:
        database = lib.get_db()
    except Exception as e:
        LOGGER.error("Failed to connect to the db: {}".format(e))

    wallet_dir = CONFIG[PROCESS]["wallet_dir"]
    minimum_payout = int(CONFIG[PROCESS]["minimum_payout"])
    os.chdir(wallet_dir)
    utxos = Pool_utxo.getPayable(minimum_payout)
    database.db.getSession().commit()
    # XXX TODO: Use the current balance, timestamp, the last_attempt timestamp, last_payout, and failed_attempts
    # XXX TODO: to filter and sort by order we want to make payment attempts
    for utxo in utxos:
        try:
            # Try less often for wallets that dont answer
            if utxo.amount < utxo.failure_count:
                if randint(0, 11) != 0:
                    continue
            LOGGER.warn("Trying to pay: {} {} {}".format(utxo.id, utxo.address, utxo.amount))
            # Lock just this current record for update
            locked_utxo = Pool_utxo.get_locked_by_id(utxo.id)
            # Save and Zero the balance
            original_balance = locked_utxo.amount
            locked_utxo.amount = 0
            # Savepoint changes - if we crash after sending coins but before commit we roll back to here.
            #   The pool audit service (coming soon) finds lost payouts and restores user balance
            database.db.getSession().begin_nested();
            # Attempt to make the payment
            timestamp = datetime.utcnow()
            status =  makePayout(locked_utxo.address, original_balance)
            LOGGER.warn("Payout status: {}".format(status))
            if status == 0:
                LOGGER.warn("Made payout for {} {} {} at {}".format(locked_utxo.id, locked_utxo.address, original_balance, timestamp))
                # Create a payment record
                payment_record = Pool_payment(locked_utxo.id, timestamp, locked_utxo.address, original_balance, 0, locked_utxo.failure_count, "schedule" )
                database.db.getSession().add(payment_record)
                # Update timestamp of last payout, number of failed payout attempts
                locked_utxo.amount = 0
                locked_utxo.failure_count = 0
                locked_utxo.last_try = timestamp
                locked_utxo.last_success = timestamp
                locked_utxo.total_amount += original_balance
                # Commit changes
                database.db.getSession().commit() 
            else:
                LOGGER.error("Failed to make payout: {} {} {}".format(locked_utxo.id, locked_utxo.address, original_balance))
                # Restore the users balance 
                locked_utxo.amount = original_balance
                # Update number of failed payout attempts
                if locked_utxo.failure_count is None:
                    locked_utxo.failure_count = 0
                locked_utxo.failure_count += 1
                locked_utxo.last_try = timestamp
                # Commit changes
                database.db.getSession().commit()
            database.db.getSession().commit()

        except Exception as e:
            LOGGER.error("Failed to process utxo: {} because {}".format(utxo.id, str(e)))
            database.db.getSession().rollback()
            sys.exit(1)

    LOGGER.warn("=== Completed {}".format(PROCESS))
示例#19
0
def main():
    global LOGGER
    global CONFIG
    CONFIG = lib.get_config()
    LOGGER = lib.get_logger(PROCESS)

    while True:
        try:
            LOGGER.warn("=== Starting {}".format(PROCESS))

            # Connect to DB
            database = lib.get_db()

            # Get the prebious audit record to find its height
            previous_audit_record = Pool_audit.getLatest()
            if previous_audit_record is None:
                previous_audit_record = Pool_audit()
                database.db.createDataObj(previous_audit_record)

            # Create new pool audit record
            audit_record = Pool_audit()

            summary_info = wallet.retrieve_summary_info(refresh=True)

            # Set the height by wallet
            audit_record.height = int(summary_info["last_confirmed_height"])
            # Set pool bock count
            audit_record.pool_blocks_count = Pool_blocks.count(
                audit_record.height) - Pool_blocks.count(
                    previous_audit_record.height)
            # Audit pools liability vs equity
            audit_record.equity = int(
                summary_info["amount_currently_spendable"]) + int(
                    summary_info["amount_awaiting_confirmation"])
            audit_record.liability = Pool_utxo.get_liability()
            audit_record.balance = audit_record.equity - audit_record.liability

            # Add payouts value
            payments_made = Pool_payment.get_by_height(
                audit_record.height,
                audit_record.height - previous_audit_record.height)
            audit_record.payouts = sum(
                [payment.amount for payment in payments_made])
            # Add payments value
            pool_credits = Pool_credits.get_by_height(
                audit_record.height,
                audit_record.height - previous_audit_record.height)
            total_credits = 0
            if pool_credits is not None:
                for credit in pool_credits:
                    credits_this_block = sum(credit.credits.values())
                    total_credits += credits_this_block
                    print("credits_this_block: {}, total_credits: {}".format(
                        credits_this_block, total_credits))
                audit_record.payments = total_credits
            else:
                audit_record.payments = 0

            # Add and Commit the audit record
            #LOGGER.warn("Create Audit Record: {}".format(json.dumps(audit_record)))
            database.db.createDataObj(audit_record)

            LOGGER.warn("=== Completed {}".format(PROCESS))
        except Exception as e:
            lib.teardown_db()
            LOGGER.exception("Something went wrong: {} ".format(
                traceback.format_exc()))

        time.sleep(999)
示例#20
0
def main():
    global LOGGER
    LOGGER = lib.get_logger(PROCESS)
    LOGGER.warn("=== Starting {}".format(PROCESS))

    # Connect to DB
    database = lib.get_db()

    latest_block = 0

    # XXX All in one db transaction....
    # Get unlocked blocks from the db
    unlocked_blocks = Pool_blocks.get_all_unlocked()
    database.db.getSession().commit()
    for pb in unlocked_blocks:
        try:
            LOGGER.warn("Processing unlocked block: {}".format(pb))
            if pb.height > latest_block:
                latest_block = pb.height
            # Get Worker_stats of this block + range to calculate reward for each worker
            worker_shares_window = Worker_shares.get_by_height(pb.height, PPLNS_WINDOW)
            print("worker_shares_window = {}".format(worker_shares_window))
            # Calculate Payment info:
            if len(worker_shares_window) > 0:
                # Calcualte reward/share:
                # XXX TODO: Enhance
                #  What algorithm to use?  Maybe: https://slushpool.com/help/manual/rewards
                # For now, some variation on pplns

                # Sum up the number of each size share submitted by each user
                shares_count_map = {}
                for worker_shares_rec in worker_shares_window:
                    if not worker_shares_rec.worker in shares_count_map:
                        shares_count_map[worker_shares_rec.worker] = {}
                    for pow_size in worker_shares_rec.sizes():
                        print("pow_size = {}".format(pow_size))
                        if not pow_size in shares_count_map[worker_shares_rec.worker]:
                            shares_count_map[worker_shares_rec.worker][pow_size] = 0
                        shares_count_map[worker_shares_rec.worker][pow_size] += worker_shares_rec.num_valid(pow_size)
                print("Shares Count Map:")
                pp.pprint(shares_count_map)

                # Normalize and sum each workers shares to create a "share value"
                total_value = 0
                for worker, worker_shares_count in shares_count_map.items():
                    print("worker: {}, worker_shares_count: {}".format(worker, worker_shares_count))
                    sizes = list(worker_shares_count.keys())
                    print("sizes: {}".format(sizes))
                    shares_count_map[worker]["value"] = 0
                    value = 0
                    for size, count in worker_shares_count.items():
                        if size == 29:
                            value += float(count) * .33
                        else:
                            value += float(count)
                        total_value += value
                    shares_count_map[worker]["value"] = value
                    print("Worker {} value: {}".format(worker, value))
                
                # Make payments based on the workers total share_value
                for worker, worker_shares_count in shares_count_map.items():
                    worker_rewards = REWARD * worker_shares_count["value"] / total_value
                    # Add or create worker rewards
                    worker_utxo = Pool_utxo.credit_worker(worker, worker_rewards)
                    LOGGER.warn("Credit to user: {} = {}".format(worker, worker_rewards))
            # Mark the pool_block state="paid" (maybe "processed" would be more accurate?)
            pb.state = "paid"
            database.db.getSession().commit()
        except Exception as e:
            database.db.getSession().rollback()
            LOGGER.error("Something went wrong: {} - {}".format(e, traceback.print_exc()))

    #database.db.getSession().commit()
    LOGGER.warn("=== Completed {}".format(PROCESS))
    sys.stdout.flush()
示例#21
0
def get_tx_slate(user_id, logger, database, method, invoked_by):

    # 1) Create a Slate
    slate = None
    try:
        locked_utxo = Pool_utxo.get_locked_by_userid(user_id)
        if locked_utxo is None or locked_utxo.amount < (1 * 1000000000):
            message = "Insufficient available balance for payout"
            logger.warn(message)
            raise PaymentError(400, message)
        amount = locked_utxo.amount
        # Generate a slate file
        try:
            args = {
                'src_acct_name': None,
                'amount': int(amount),
                'minimum_confirmations': 10,
                'max_outputs': 10,
                'num_change_outputs': 1,
                'selection_strategy_is_use_all': False,
                'message': "pool payment: slate: user_id={}".format(user_id),
                'target_slate_version': None,
                'send_args': None,
            }
            logger.warn(
                "Requesting Payment slate from payment request api: {}".format(
                    args))
            slate = wallet.init_send_tx(args)
        except Exception as e:
            logger.exception("Failed to get a payment slate: {}".format(
                str(e)))
            raise PaymentError(500, str(e))
    except PaymentError as e:  # My own errors
        raise
    except Exception as e:
        logger.exception("Failed to get a payment slate: {}".format(str(e)))
        raise PaymentError(500, str(e))

    # 2) Create a payment record
    try:
        timestamp = datetime.utcnow()
        payment_record = Pool_payment(
            user_id=locked_utxo.user_id,
            timestamp=timestamp,
            height=slate["height"],
            address=slate["id"],
            amount=amount,
            method=method,
            fee=slate["fee"],
            failure_count=locked_utxo.failure_count,
            state="sent",
            tx_data=json.dumps(slate),
            invoked_by=invoked_by,
        )
        database.db.getSession().add(payment_record)
        # Update the users utxo record
        locked_utxo.amount = int(slate["fee"]) * -1
        locked_utxo.last_try = timestamp
        locked_utxo.total_amount += amount
        database.db.getSession().commit()
    except Exception as e:
        logger.exception("Failed to create payment record: {}".format(str(e)))
        raise PaymentError(500, str(e))

    # 3) Lock the wallet outputs
    try:
        wallet.tx_lock_outputs(slate)
    except Exception as e:
        logger.exception("Failed to lock wallet outputs: {}".format(str(e)))
        raise PaymentError(500, str(e))

    # Return the slate
    return slate
示例#22
0
from grinbase.constants.MysqlConstants import MysqlConstants
from grinbase.dbaccess import database
from grinbase.dbaccess.database import database_details
from grinbase.model.pool_utxo import Pool_utxo

if __name__ == '__main__':
    database.db = database_details(MYSQL_CONSTANTS=MysqlConstants())
    database.db.initialize()

#    for i in range(0,10):
#        tmp = Pool_utxo(id=str(i), address=str(i), amount=1.5*i)
#        database.db.createDataObj(tmp)


    utxo = Pool_utxo.getPayable(0)[0]
    print(utxo)
    locked_utxo = Pool_utxo.get_locked_by_id(utxo.id)
    print(locked_utxo)
    locked_utxo.amount=1.0
    database.db.getSession().begin_nested();
    locked_utxo.amount=7.0
    database.db.getSession().commit()
    database.db.getSession().commit()

    utxo = Pool_utxo.getPayable(0)[0]
    print(utxo)


#    for utxo in Pool_utxo.getPayable(0):
#        Pool_utxo.get_locked_by_id(utxo.id)