def main(): # Creating and loading tests ad = Address.create(PW, LABEL) ad_b = Address.load(PW, LABEL) print(ad.public_key == ad_b.public_key) print(ad.raw == ad_b.raw) # Wallet tests wallet = Wallet() wallet.sign_up(PW2, LABEL2) print(wallet.current_address.label) wallet2 = Wallet() wallet2.log_in(PW2, LABEL2) print(wallet2.current_address.label) # Transaction test transaction = wallet.create_transaction(ad.raw, 20) print( '\tReceiver: {0}\n\tAmount: {1}\n\tSignature: {2}\n\t Timestamp: {3}'. format(transaction.receiver, transaction.amount, transaction.signature, transaction.timestamp)) # Signature verification example (and therefore test) verify_signature(transaction) transaction2 = Transaction.parse(Transaction.serialize(transaction)) transaction.sender_public_key = ad.public_key # Changing the public key so signature shouldn't be correct verify_signature(transaction) print("TX2 verify", transaction2.verify_signature())
def create(self, validated_data): validated_data["transaction_id"] = uuid4().hex instance = Transaction(**validated_data) instance.save() counter_validated_data = copy(validated_data) counter_validated_data["account"] = validated_data["counter_account"] counter_validated_data["counter_account"] = validated_data["account"] counter_validated_data["debit"] = validated_data["credit"] counter_validated_data["credit"] = validated_data["debit"] counter_instance = Transaction(**counter_validated_data) counter_instance.save() return instance
def get(self, request): server = Relay() if 'exclude_hash' in request.data: exclude = request.data['exclude_hash'] else: exclude = [] logger.debug("'exclude_hash' not in request.") transaction = server.get_transaction(exclude) if transaction is not None: return Response(Transaction.serialize(transaction), status=status.HTTP_200_OK) else: return Response({'errors': 'no transaction to send'}, status=status.HTTP_404_NOT_FOUND)
def post(self, request): """ Add a new transaction to the relay transactions list. If the transaction.hash is already in the list, it will not be added and return 406 :param request: :return: 201 if transaction added, 406 otherwise (hash already exist) """ server = Relay() try: transaction = Transaction.parse(request.data) server.add_transaction(transaction) return Response(status=status.HTTP_201_CREATED) except (RelayError, ParseException) as e: return Response(str(e), status=status.HTTP_406_NOT_ACCEPTABLE)
def delete(self, request): """ Delete transactions receive in request.data['transactions'] This is usually done when masternode receive an incorrect block and reject some transaction. :param request: :return: 2OO (OK) """ # TODO make sure that only masters can request deletes server = Relay() for tx_data in request.data['transactions']: try: transaction = Transaction.parse(tx_data) except ParseException as e: logger.warning("Cannot remove transaction: %s" % (str(e))) else: logger.debug("Removing transaction %s " % transaction.hash) server.remove_transaction(transaction) return Response(status=status.HTTP_200_OK)
def settlement(merchant_id, wechat_amount, alipay_amount): assert merchant_id is not None assert wechat_amount is not None assert alipay_amount is not None with transaction.atomic(): now_timestamp = timezone.now() merchant_account_id = Merchant.objects.get( pk=merchant_id).account_id merchant_account = Account.objects.select_for_update().get( id=merchant_account_id) # 最近一条结算账单记账日期必须小于当前记账时间,避免重复结算 latest_settlement = Settlement.objects.filter(account__id=merchant_account_id)\ .filter(status=SETTLEMENT_STATUS['PROCESSING']).order_by('-datetime').first() if latest_settlement and latest_settlement.datetime.date( ) >= now_timestamp.date(): raise SettlementDuplicateException( f"{now_timestamp.strftime('%Y-%m-%d %H:%M:%S.%f')} occur duplicate settlement" f",merchant's account's id is {merchant_account_id}," f" wechat_amount is {wechat_amount} fen, alipay_amount is {alipay_amount} fen" ) # 校验金额 if merchant_account.balance < wechat_amount or \ merchant_account.withdrawable_balance < wechat_amount or \ merchant_account.alipay_balance < alipay_amount or \ merchant_account.alipay_withdrawable_balance < alipay_amount: raise SettlementAbnormalBalanceException( f"{now_timestamp.strftime('%Y-%m-%d %H:%M:%S.%f')} occur abnormal balance settlement" f",merchant's account's id is {merchant_account_id}," f" wechat_amount is {wechat_amount} fen, alipay_amount is {alipay_amount} fen" ) # 账户扣钱 if wechat_amount > 0: merchant_account.balance = merchant_account.balance - wechat_amount merchant_account.withdrawable_balance = merchant_account.withdrawable_balance - wechat_amount if alipay_amount > 0: merchant_account.alipay_balance = merchant_account.alipay_balance - alipay_amount merchant_account.alipay_withdrawable_balance = merchant_account.alipay_withdrawable_balance - alipay_amount merchant_account.save() new_settlement = Settlement.objects.create( serial_number=generate_serial_number(), datetime=now_timestamp, finished_datetime=None, status=SETTLEMENT_STATUS['PROCESSING'], account=merchant_account, wechat_amount=wechat_amount, alipay_amount=alipay_amount, ) Transaction.objects.bulk_create([ Transaction( content_object=new_settlement, transaction_type=TRANSACTION_TYPE[ 'MERCHANT_WECHAT_SETTLEMENT'], datetime=now_timestamp, account=merchant_account, amount=-wechat_amount, balance_after_transaction=merchant_account.balance), Transaction( content_object=new_settlement, transaction_type=TRANSACTION_TYPE[ 'MERCHANT_ALIPAY_SETTLEMENT'], datetime=now_timestamp, account=merchant_account, amount=-alipay_amount, balance_after_transaction=merchant_account.alipay_balance) ])
def create_transaction(self, destination_address, amount): new_transaction = Transaction(destination_address, amount, datetime.now().timestamp(), self.current_address.public_key) self.sign_transaction(new_transaction) return new_transaction
def send_transaction(transaction): return requests.post(relay_addr(TRANSACTION_ENDPOINT), data=Transaction.serialize(transaction)).status_code
def post(self, request): """ Manage the POST request, the Master Node will receive a block and it will check wether it is accepted or not and will add it to the rest of the blockchain accordingly only if the one requesting it is a Relay Node (see user and password above). If the block is rejected because of bad transactions, those transactions are returned to the relay node that made the request. If the block is accepted, the new block is sent to all relay nodes. """ try: # request contains the block, and the address of the miner logger.debug("Block received from %s" % request.data['miner_address']) block_data = request.data['block'] block = Block.parse(block_data) except KeyError: logger.debug("No block given.") return Response({"errors": "No block given."}, status=status.HTTP_406_NOT_ACCEPTABLE) except ParseException as e: logger.debug("Parsing block error.") return Response({"errors": "%s" % e}, status=status.HTTP_406_NOT_ACCEPTABLE) (hash_verify, bad_transactions) = self.server.update_blockchain(block) if hash_verify and len(bad_transactions) == 0: # block is valid logger.debug("Block '%s' successfully added" % block.header) data = {'transactions': []} for transaction in block.transactions: data['transactions'].append(Transaction.serialize(transaction)) for relay_ip in settings.RELAY_IP: logger.debug("Sending block '%s' to relay %s" % (block.header, relay_ip)) client.post(relay_ip, 'blockchain', block_data) client.delete(relay_ip, 'transactions', data) if self.server.balance >= settings.REWARD: self.server.balance -= settings.REWARD miner_transaction = self.server.wallet.create_transaction( request.data['miner_address'], settings.REWARD) client.post(relay_ip, 'transactions', Transaction.serialize(miner_transaction)) response = Response({"detail": "Block successfully added!"}, status=status.HTTP_201_CREATED) else: logger.debug("Block '%s' can NOT be added (bad header or bad TXs)." % block.header) data = {'transactions': []} if len(bad_transactions) > 0: for transaction in bad_transactions: logger.debug("Bad TX '%s'" % transaction.hash) data['transactions'].append(Transaction.serialize(transaction)) # Send to all relays bad TXs, since the miner can request # transactions from any relay for relay_ip in settings.RELAY_IP: logger.debug("Sending bad TXs to relay %s" % relay_ip) client.delete(relay_ip, 'transactions', data) response = Response({"errors": "Bad transactions where found in new block.", "data": data}, status=status.HTTP_406_NOT_ACCEPTABLE) else: response = Response({"errors": "Bad header.", "data": block_data}, status=status.HTTP_406_NOT_ACCEPTABLE) return response
def get_transaction(self): return Transaction()
def on_payment_unfreeze(cls, payment): """ unfreeze payment. :param payment: :return: :raise: InvalidStatusError """ with PayChannelContext(payment.pay_channel): with transaction.atomic(): timestamp = timezone.now() accounts = None merchant_account = None if payment.coupon: # 使用了优惠券的情况 merchant_account_id = payment.merchant.account_id originator_account_id = payment.coupon.originator_merchant.account_id inviter_account_id = payment.merchant.inviter.account_id accounts = AccountProxy.objects.select_for_update().filter( id__in=(PLATFORM_ACCOUNT_ID, merchant_account_id, originator_account_id, inviter_account_id)) else: merchant_account_id = payment.merchant.account_id merchant_account = AccountProxy.objects.select_for_update( ).get(id=merchant_account_id) payment = Payment.objects.select_for_update().select_related( 'coupon').get(serial_number=payment.serial_number) if payment.status != PAYMENT_STATUS.FROZEN: raise InvalidStatusError() if payment.coupon: # 使用了优惠券的情况 accounts = {a.id: a for a in accounts} if len(accounts) != 4: logger.error('Cannot find all the accounts:{}'.format( repr((PLATFORM_ACCOUNT_ID, merchant_account_id, originator_account_id, inviter_account_id)))) raise AccountProxy.DoesNotExist() platform_account = accounts[PLATFORM_ACCOUNT_ID] merchant_account = accounts[merchant_account_id] originator_account = accounts[originator_account_id] inviter_account = accounts[inviter_account_id] platform_account.channel_balance -= payment.originator_share + payment.inviter_share platform_account.save() originator_account.channel_balance += payment.originator_share originator_account.channel_withdrawable_balance += payment.originator_share originator_account.save() inviter_account.channel_balance += payment.inviter_share inviter_account.channel_withdrawable_balance += payment.inviter_share inviter_account.save() payment.status = PAYMENT_STATUS.FINISHED payment.save() merchant_account.channel_withdrawable_balance += payment.order_price - payment.coupon.discount - sum( (payment.platform_share, payment.originator_share, payment.inviter_share)) merchant_account.save() # 记录Transaction Transaction.objects.bulk_create([ Transaction(content_object=payment, transaction_type=TRANSACTION_TYPE[ 'PLATFORM_EXPEND_MERCHANT_SHARE'], datetime=timestamp, account=platform_account, amount=-payment.originator_share, balance_after_transaction=platform_account. channel_balance + payment.inviter_share), Transaction(content_object=payment, transaction_type=TRANSACTION_TYPE[ 'PLATFORM_EXPEND_MARKETER_SHARE'], datetime=timestamp, account=platform_account, amount=-payment.inviter_share, balance_after_transaction=platform_account. channel_balance), Transaction(content_object=payment, transaction_type=TRANSACTION_TYPE[ 'PLATFORM_EXPEND_PLATFORM_SHARE'], datetime=timestamp, account=platform_account, amount=-payment.platform_share, balance_after_transaction=platform_account. channel_balance - payment.platform_share), Transaction(content_object=payment, transaction_type=TRANSACTION_TYPE[ 'PLATFORM_SHARE'], datetime=timestamp, account=platform_account, amount=payment.platform_share, balance_after_transaction=platform_account. channel_balance), Transaction( content_object=payment, transaction_type=TRANSACTION_TYPE[ 'MERCHANT_SHARE'], datetime=timestamp, account=originator_account, amount=payment.originator_share, balance_after_transaction=originator_account. channel_balance), Transaction(content_object=payment, transaction_type=TRANSACTION_TYPE[ 'MARKETER_SHARE'], datetime=timestamp, account=inviter_account, amount=payment.inviter_share, balance_after_transaction=inviter_account. channel_balance) ]) else: # 没有使用优惠券的情况 payment.status = PAYMENT_STATUS.FINISHED payment.save() merchant_account.channel_withdrawable_balance += payment.order_price merchant_account.save()
def on_payment_success(cls, payment): """ :param payment: :return: """ with PayChannelContext(payment.pay_channel): with transaction.atomic(): timestamp = timezone.now() platform_account = AccountProxy.objects.select_for_update( ).get(id=PLATFORM_ACCOUNT_ID) merchant_account = AccountProxy.objects.select_for_update( ).get(id=payment.merchant.account.id) payment = Payment.objects.select_for_update().get( serial_number=payment.serial_number) if payment.status != PAYMENT_STATUS['UNPAID']: raise InvalidStatusError() # Duplicated callback payment.status = PAYMENT_STATUS['FROZEN'] payment.save() if not payment.coupon: # 没有使用优惠券的情况 merchant_account.channel_balance = merchant_account.channel_balance + payment.order_price merchant_account.save() Transaction.objects.bulk_create([ Transaction(content_object=payment, transaction_type=TRANSACTION_TYPE[ 'PLATFORM_RECEIVE'], datetime=timestamp, account=platform_account, amount=payment.order_price, balance_after_transaction=platform_account. channel_balance + payment.order_price), Transaction(content_object=payment, transaction_type=TRANSACTION_TYPE[ 'PLATFORM_EXPEND_MERCHANT_RECEIVE'], datetime=timestamp, account=platform_account, amount=-payment.order_price, balance_after_transaction=platform_account. channel_balance), Transaction(content_object=payment, transaction_type=TRANSACTION_TYPE[ 'MERCHANT_RECEIVE'], datetime=timestamp, account=merchant_account, amount=payment.order_price, balance_after_transaction=merchant_account. channel_balance) ]) else: # 使用了优惠券的情况 paid_price = payment.order_price - payment.coupon.discount total_share = payment.platform_share + payment.inviter_share + payment.originator_share platform_account.channel_balance = platform_account.channel_balance + total_share platform_account.save() merchant_account.channel_balance = merchant_account.channel_balance + paid_price - total_share merchant_account.save() Transaction.objects.bulk_create([ Transaction(content_object=payment, transaction_type=TRANSACTION_TYPE[ 'PLATFORM_RECEIVE'], datetime=timestamp, account=platform_account, amount=paid_price, balance_after_transaction=platform_account. channel_balance + paid_price - total_share), Transaction(content_object=payment, transaction_type=TRANSACTION_TYPE[ 'PLATFORM_EXPEND_MERCHANT_RECEIVE'], datetime=timestamp, account=platform_account, amount=-(paid_price - total_share), balance_after_transaction=platform_account. channel_balance), Transaction(content_object=payment, transaction_type=TRANSACTION_TYPE[ 'MERCHANT_RECEIVE'], datetime=timestamp, account=merchant_account, amount=paid_price - total_share, balance_after_transaction=merchant_account. channel_balance) ])
def on_refund_success(cls, refund): with transaction.atomic(): timestamp = timezone.now() platform_account = AccountProxy.objects.select_for_update().get( id=PLATFORM_ACCOUNT_ID) merchant_account = AccountProxy.objects.select_for_update().get( id=refund.payment.merchant.account.id) coupon = refund.payment.coupon if coupon: coupon = Coupon.objects.select_for_update().get(id=coupon.id) payment = Payment.objects.select_for_update().get( serial_number=refund.payment.serial_number) refund = Refund.objects.select_for_update().get( serial_number=refund.serial_number) with PayChannelContext(payment.pay_channel): if refund.status != REFUND_STATUS['REQUESTED'] \ or payment.status != PAYMENT_STATUS['REFUND_REQUESTED']: raise InvalidStatusError() refund.status = REFUND_STATUS['FINISHED'] refund.save() payment.status = PAYMENT_STATUS['REFUND'] payment.save() if not coupon: # 没有使用优惠券 merchant_account.channel_balance = merchant_account.channel_balance - payment.order_price merchant_account.save() Transaction.objects.bulk_create([ Transaction(content_object=refund, transaction_type=TRANSACTION_TYPE[ 'MERCHANT_REFUND'], datetime=timestamp, account=merchant_account, amount=-payment.order_price, balance_after_transaction=merchant_account. channel_balance), Transaction(content_object=refund, transaction_type=TRANSACTION_TYPE[ 'PLATFORM_EARNING_MERCHANT_REFUND'], datetime=timestamp, account=platform_account, amount=payment.order_price, balance_after_transaction=platform_account. channel_balance + payment.order_price), Transaction(content_object=refund, transaction_type=TRANSACTION_TYPE[ 'PLATFORM_REFUND'], datetime=timestamp, account=platform_account, amount=-payment.order_price, balance_after_transaction=platform_account. channel_balance) ]) else: # 有使用优惠券的情况 paid_price = payment.order_price - payment.coupon.discount total_share = payment.platform_share + payment.inviter_share + payment.originator_share merchant_account.channel_balance = merchant_account.channel_balance - ( paid_price - total_share) merchant_account.save() platform_account.channel_balance = platform_account.channel_balance - total_share platform_account.save() Transaction.objects.bulk_create([ Transaction(content_object=refund, transaction_type=TRANSACTION_TYPE[ 'MERCHANT_REFUND'], datetime=timestamp, account=merchant_account, amount=-(paid_price - total_share), balance_after_transaction=merchant_account. channel_balance), Transaction(content_object=refund, transaction_type=TRANSACTION_TYPE[ 'PLATFORM_EARNING_MERCHANT_REFUND'], datetime=timestamp, account=platform_account, amount=(paid_price - total_share), balance_after_transaction=platform_account. channel_balance + paid_price), Transaction(content_object=refund, transaction_type=TRANSACTION_TYPE[ 'PLATFORM_REFUND'], datetime=timestamp, account=platform_account, amount=-paid_price, balance_after_transaction=platform_account. channel_balance) ]) # 设置原优惠券为销毁状态,并退给用户一张新的优惠券 coupon.status = COUPON_STATUS.DESTROYED coupon.save() new_coupon = Coupon.objects.create( rule_id=coupon.rule_id, client_id=coupon.client_id, discount=coupon.discount, min_charge=coupon.min_charge, originator_merchant_id=coupon.originator_merchant_id, status=COUPON_STATUS.NOT_USED, obtain_datetime=coupon.obtain_datetime, )