Esempio n. 1
0
def process_outgoing_transactions():
    with CacheLock('process_outgoing_transactions'):
        update_wallets = []
        for ot in OutgoingTransaction.objects.filter(executed_at=None):
            result = None
            OutgoingTransaction.objects.filter(id=ot.id).update(executed_at=datetime.datetime.now(), txid=result)
            db_transaction.commit()
            try:
                result = bitcoind.send(ot.to_bitcoinaddress, ot.amount)
            except jsonrpc.JSONRPCException:
                raise
            OutgoingTransaction.objects.filter(id=ot.id).update(txid=result)
            transaction = bitcoind.gettransaction(result)
            if Decimal(transaction['fee']) < Decimal(0):
                wt = ot.wallettransaction_set.all()[0]
                fee_transaction = WalletTransaction.objects.create(
                    amount=Decimal(transaction['fee']) * Decimal(-1),
                    from_wallet_id=wt.from_wallet_id)
                update_wallets.append(wt.from_wallet_id)
        db_transaction.commit()

        for wid in update_wallets:
            if getattr(django_settings, "CELERY_ALWAYS_EAGER", False):
                # Do not do asyncrhonous transaction processing
                update_wallet_balance(wid)
                db_transaction.commit()
            else:
                update_wallet_balance.delay(wid)
Esempio n. 2
0
    def send_to_address(self, address, amount, description=''):
        if settings.BITCOIN_DISABLE_OUTGOING:
            raise Exception("Outgoing transactions disabled! contact support.")
        address = address.strip()

        if type(amount) != Decimal:
            amount = Decimal(amount)
        amount = amount.quantize(Decimal('0.00000001'))

        if not is_valid_btc_address(str(address)):
            raise Exception(_("Not a valid bitcoin address") + ":" + address)
        if amount <= 0:
            raise Exception(_("Can't send zero or negative amounts"))
        # concurrency check
        with db_transaction.autocommit():
            db_transaction.enter_transaction_management()
            db_transaction.commit()
            avail = self.total_balance()
            updated = Wallet.objects.filter(Q(id=self.id)).update(last_balance=avail)
            if amount > avail:
                raise Exception(_("Trying to send too much"))
            new_balance = avail - amount
            updated = Wallet.objects.filter(Q(id=self.id) & Q(transaction_counter=self.transaction_counter) & 
                Q(last_balance=avail) )\
              .update(last_balance=new_balance, transaction_counter=self.transaction_counter+1)
            if not updated:
                print "address transaction concurrency:", new_balance, avail, self.transaction_counter, self.last_balance, self.total_balance()
                raise Exception(_("Concurrency error with transactions. Please try again."))
            # concurrency check end
            bwt = WalletTransaction.objects.create(
                amount=amount,
                from_wallet=self,
                to_bitcoinaddress=address,
                description=description)
            try:
                result = bitcoind.send(address, amount)
            except jsonrpc.JSONRPCException:
                bwt.delete()
                raise
            self.transaction_counter = self.transaction_counter+1
            self.last_balance = new_balance
        
            # check if a transaction fee exists, and deduct it from the wallet
            # TODO: because fee can't be known beforehand, can result in negative wallet balance.
            # currently isn't much of a issue, but might be in the future, depending of the application
            transaction = bitcoind.gettransaction(result)
            fee_transaction = None
            total_amount = amount
            if Decimal(transaction['fee']) < Decimal(0):
                fee_transaction = WalletTransaction.objects.create(
                    amount=Decimal(transaction['fee']) * Decimal(-1),
                    from_wallet=self)
                total_amount += fee_transaction.amount
            if settings.BITCOIN_TRANSACTION_SIGNALING:
                balance_changed.send(sender=self, 
                    changed=(Decimal(-1) * total_amount), transaction=bwt)
                balance_changed_confirmed.send(sender=self, 
                    changed=(Decimal(-1) * total_amount), transaction=bwt)
            return (bwt, fee_transaction)
Esempio n. 3
0
    def send_to_address(self, address, amount, description=''):
        if settings.BITCOIN_DISABLE_OUTGOING:
            raise Exception("Outgoing transactions disabled! contact support.")
        address = address.strip()

        if type(amount) != Decimal:
            amount = Decimal(amount)
        amount = amount.quantize(Decimal('0.00000001'))

        if not is_valid_btc_address(str(address)):
            raise Exception(_("Not a valid bitcoin address") + ":" + address)
        if amount <= 0:
            raise Exception(_("Can't send zero or negative amounts"))
        # concurrency check
        with db_transaction.autocommit():
            avail = self.total_balance()
            updated = Wallet.objects.filter(Q(id=self.id)).update(last_balance=avail)
            if amount > avail:
                raise Exception(_("Trying to send too much"))
            new_balance = avail - amount
            updated = Wallet.objects.filter(Q(id=self.id) & Q(transaction_counter=self.transaction_counter) & 
                Q(last_balance=avail) )\
              .update(last_balance=new_balance, transaction_counter=self.transaction_counter+1)
            if not updated:
                print "address transaction concurrency:", new_balance, avail, self.transaction_counter, self.last_balance, self.total_balance()
                raise Exception(_("Concurrency error with transactions. Please try again."))
            # concurrency check end
            bwt = WalletTransaction.objects.create(
                amount=amount,
                from_wallet=self,
                to_bitcoinaddress=address,
                description=description)
            try:
                result = bitcoind.send(address, amount)
            except jsonrpc.JSONRPCException:
                bwt.delete()
                raise
            self.transaction_counter = self.transaction_counter+1
            self.last_balance = new_balance
        
            # check if a transaction fee exists, and deduct it from the wallet
            # TODO: because fee can't be known beforehand, can result in negative wallet balance.
            # currently isn't much of a issue, but might be in the future, depending of the application
            transaction = bitcoind.gettransaction(result)
            fee_transaction = None
            total_amount = amount
            if Decimal(transaction['fee']) < Decimal(0):
                fee_transaction = WalletTransaction.objects.create(
                    amount=Decimal(transaction['fee']) * Decimal(-1),
                    from_wallet=self)
                total_amount += fee_transaction.amount
            if settings.BITCOIN_TRANSACTION_SIGNALING:
                balance_changed.send(sender=self, 
                    changed=(Decimal(-1) * total_amount), transaction=bwt)
                balance_changed_confirmed.send(sender=self, 
                    changed=(Decimal(-1) * total_amount), transaction=bwt)
            return (bwt, fee_transaction)
Esempio n. 4
0
def process_outgoing_transactions_group():
    if OutgoingTransaction.objects.filter(executed_at=None, expires_at__lte=datetime.datetime.now()).count()>0 or \
        OutgoingTransaction.objects.filter(executed_at=None).count()>6:
        with NonBlockingCacheLock('process_outgoing_transactions'):
            ots = OutgoingTransaction.objects.filter(executed_at=None).order_by("expires_at")[:7]
            ots_ids = [ot.id for ot in ots]
            update_wallets = []
            transaction_hash = {}
            for ot in ots:
                transaction_hash[ot.to_bitcoinaddress] = float(ot.amount)
            updated = OutgoingTransaction.objects.filter(id__in=ots_ids,
                executed_at=None).select_for_update().update(executed_at=datetime.datetime.now())
            db_transaction.commit()
            if updated == len(ots):
                try:
                    result = bitcoind.sendmany(transaction_hash)
                except jsonrpc.JSONRPCException as e:
                    if e.error == u"{u'message': u'Insufficient funds', u'code': -4}":
                        u2 = OutgoingTransaction.objects.filter(id__in=ots_ids, under_execution=False
                            ).select_for_update().update(executed_at=None)
                        db_transaction.commit()
                    else:
                        u2 = OutgoingTransaction.objects.filter(id__in=ots_ids, under_execution=False).select_for_update().update(under_execution=True, txid=e.error)
                        # print "error updating", e.error, u2, ots_ids
                        db_transaction.commit()
                    raise
                OutgoingTransaction.objects.filter(id__in=ots_ids).update(txid=result)
                db_transaction.commit()
                transaction = bitcoind.gettransaction(result)
                if Decimal(transaction['fee']) < Decimal(0):
                    fw = fee_wallet()
                    fee_amount = Decimal(transaction['fee']) * Decimal(-1)
                    orig_fee_transaction = WalletTransaction.objects.create(
                            amount=fee_amount,
                            from_wallet=fw,
                            to_wallet=None)
                    i = 1
                    for ot_id in ots_ids:
                        wt = WalletTransaction.objects.get(outgoing_transaction__id=ot_id)
                        update_wallets.append(wt.from_wallet_id)
                        fee_transaction = WalletTransaction.objects.create(
                            amount=(fee_amount / Decimal(i)).quantize(Decimal("0.00000001")),
                            from_wallet_id=wt.from_wallet_id,
                            to_wallet=fw,
                            description="fee")
                        i += 1
            db_transaction.commit()
            for wid in update_wallets:
                update_wallet_balance.delay(wid)
    elif OutgoingTransaction.objects.filter(executed_at=None).count()>0:
        next_run_at = OutgoingTransaction.objects.filter(executed_at=None).aggregate(Min('expires_at'))['expires_at__min']
        if next_run_at:
            process_outgoing_transactions.retry(
                countdown=((next_run_at - datetime.datetime.now()) + datetime.timedelta(seconds=5)).total_seconds() )
Esempio n. 5
0
def process_outgoing_transactions():
    if OutgoingTransaction.objects.filter(executed_at=None, expires_at__lte=datetime.datetime.now()).count()>0 or \
        OutgoingTransaction.objects.filter(executed_at=None).count()>6:
        blockcount = bitcoind.bitcoind_api.getblockcount()
        with NonBlockingCacheLock('process_outgoing_transactions'):
            ots_ids = filter_doubles(OutgoingTransaction.objects.filter(executed_at=None).order_by("expires_at")[:15])
            ots = OutgoingTransaction.objects.filter(executed_at=None, id__in=ots_ids)
            update_wallets = []
            transaction_hash = {}
            for ot in ots:
                transaction_hash[ot.to_bitcoinaddress] = float(ot.amount)
            updated = OutgoingTransaction.objects.filter(id__in=ots_ids,
                executed_at=None).select_for_update().update(executed_at=datetime.datetime.now())
            if updated == len(ots):
                try:
                    result = bitcoind.sendmany(transaction_hash)
                except jsonrpc.JSONRPCException as e:
                    if e.error == u"{u'message': u'Insufficient funds', u'code': -4}" or \
                        e.error == u"{u'message': u'Insufficient funds', u'code': -6}":
                        u2 = OutgoingTransaction.objects.filter(id__in=ots_ids, under_execution=False
                            ).select_for_update().update(executed_at=None)
                    else:
                        u2 = OutgoingTransaction.objects.filter(id__in=ots_ids, under_execution=False
                            ).select_for_update().update(under_execution=True, txid=e.error)
                    raise
                OutgoingTransaction.objects.filter(id__in=ots_ids).update(txid=result)
                transaction = bitcoind.gettransaction(result)
                if Decimal(transaction['fee']) < Decimal(0):
                    fw = fee_wallet()
                    fee_amount = Decimal(transaction['fee']) * Decimal(-1)
                    orig_fee_transaction = WalletTransaction.objects.create(
                            amount=fee_amount,
                            from_wallet=fw,
                            to_wallet=None)
                    i = 1
                    for ot_id in ots_ids:
                        wt = WalletTransaction.objects.get(outgoing_transaction__id=ot_id)
                        update_wallets.append(wt.from_wallet_id)
                        fee_transaction = WalletTransaction.objects.create(
                            amount=(fee_amount / Decimal(i)).quantize(Decimal("0.00000001")),
                            from_wallet_id=wt.from_wallet_id,
                            to_wallet=fw,
                            description="fee")
                        i += 1
                else:
                    raise Exception("Updated amount not matchinf transaction amount!")
            for wid in update_wallets:
                update_wallet_balance.delay(wid)
Esempio n. 6
0
def process_outgoing_transactions():
    if cache.get("process_outgoing_transactions"):
        print "process ongoing, skipping..."
        db_transaction.rollback()
        return
    if cache.get("wallet_downtime_utc"):
        db_transaction.rollback()
        return
    # try out bitcoind connection
    print bitcoind.bitcoind_api.getinfo()
    with NonBlockingCacheLock('process_outgoing_transactions'):
        update_wallets = []
        for ot in OutgoingTransaction.objects.filter(executed_at=None)[:3]:
            result = None
            updated = OutgoingTransaction.objects.filter(id=ot.id,
                executed_at=None, txid=None, under_execution=False).select_for_update().update(executed_at=datetime.datetime.now(), txid=result)
            db_transaction.commit()
            if updated:
                try:
                    result = bitcoind.send(ot.to_bitcoinaddress, ot.amount)
                    updated2 = OutgoingTransaction.objects.filter(id=ot.id, txid=None).select_for_update().update(txid=result)
                    db_transaction.commit()
                    if updated2:
                        transaction = bitcoind.gettransaction(result)
                        if Decimal(transaction['fee']) < Decimal(0):
                            wt = ot.wallettransaction_set.all()[0]
                            fee_transaction = WalletTransaction.objects.create(
                                amount=Decimal(transaction['fee']) * Decimal(-1),
                                from_wallet_id=wt.from_wallet_id)
                            update_wallets.append(wt.from_wallet_id)
                except jsonrpc.JSONRPCException as e:
                    if e.error == u"{u'message': u'Insufficient funds', u'code': -4}":
                        OutgoingTransaction.objects.filter(id=ot.id, txid=None, 
                            under_execution=False).select_for_update().update(executed_at=None)
                        db_transaction.commit()
                        # sleep(10)
                        raise
                    else:
                        OutgoingTransaction.objects.filter(id=ot.id).select_for_update().update(under_execution=True)
                        db_transaction.commit()
                        raise
                
            else:
                raise Exception("Outgoingtransaction can't be updated!")
        db_transaction.commit()
        for wid in update_wallets:
            update_wallet_balance.delay(wid)
Esempio n. 7
0
def process_outgoing_transactions():
    if OutgoingTransaction.objects.filter(executed_at=None, expires_at__lte=datetime.datetime.now()).count()>0 or \
        OutgoingTransaction.objects.filter(executed_at=None).count()>6:
        blockcount = bitcoind.bitcoind_api.getblockcount()
        with NonBlockingCacheLock('process_outgoing_transactions'):
            ots_ids = filter_doubles(OutgoingTransaction.objects.filter(executed_at=None).order_by("expires_at")[:15])
            ots = OutgoingTransaction.objects.filter(executed_at=None, id__in=ots_ids)
            update_wallets = []
            transaction_hash = {}
            for ot in ots:
                transaction_hash[ot.to_bitcoinaddress] = float(ot.amount)
            updated = OutgoingTransaction.objects.filter(id__in=ots_ids,
                executed_at=None).select_for_update().update(executed_at=datetime.datetime.now())
            if updated == len(ots):
                try:
                    result = bitcoind.sendmany(transaction_hash)
                except jsonrpc.JSONRPCException as e:
                    if e.error == u"{u'message': u'Insufficient funds', u'code': -4}" or \
                        e.error == u"{u'message': u'Insufficient funds', u'code': -6}":
                        u2 = OutgoingTransaction.objects.filter(id__in=ots_ids, under_execution=False
                            ).select_for_update().update(executed_at=None)
                    else:
                        u2 = OutgoingTransaction.objects.filter(id__in=ots_ids, under_execution=False
                            ).select_for_update().update(under_execution=True, txid=e.error)
                    raise
                OutgoingTransaction.objects.filter(id__in=ots_ids).update(txid=result)
                transaction = bitcoind.gettransaction(result)

                if Decimal(transaction['fee']) < Decimal(0):
                    fw = fee_wallet()
                    fee_amount = Decimal(transaction['fee']) * Decimal(-1)
                    orig_fee_transaction = WalletTransaction.objects.create(
                            amount=fee_amount,
                            from_wallet=fw,
                            to_wallet=None)

                    # calculate fair fee
                    wallet = None
                    i = 0
                    share = (fee_amount / len(ots_ids)).quantize(Decimal("0.00000001"))
                    wallet_transacions = WalletTransaction.objects.filter(outgoing_transaction__id__in=ots_ids).order_by('from_wallet')
                    for wt in wallet_transacions:
                        if (wt.from_wallet != wallet and wallet):
                            update_wallets.append(wallet.id)
                            fee_transaction = WalletTransaction.objects.create(
                                amount=(share * i).quantize(Decimal("0.00000001")),
                                from_wallet_id=wallet.id,
                                to_wallet=fw,
                                description="fee")
                            i = 1
                            wallet = wt.from_wallet
                        else:
                            i += 1
                            wallet = wt.from_wallet

                    else:
                        if wallet:
                            update_wallets.append(wallet.id)
                            fee_transaction = WalletTransaction.objects.create(
                                amount=(share * (i - 1) + fee_amount - share * (len(ots_ids) - 1)).quantize(Decimal("0.00000001")),
                                from_wallet_id=wallet.id,
                                to_wallet=fw,
                                description="fee")

            for wid in update_wallets:
                update_wallet_balance.delay(wid)