mirror of
				https://github.com/more-tech4-magnum-opus/backend.git
				synced 2025-11-04 01:27:35 +03:00 
			
		
		
		
	fixed blockchain, added base operations
This commit is contained in:
		
							parent
							
								
									79587c322d
								
							
						
					
					
						commit
						abbd90551a
					
				| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
DJANGO_DEBUG=yes
 | 
			
		||||
DATABASE_URL=postgres://postgres:debug@127.0.0.1:5432/moretech
 | 
			
		||||
CELERY_BROKER_URL=redis://localhost:6379/0
 | 
			
		||||
MAIN_WALLET=46d3684932f300a7fcdc8cc73cfa3057b5f61695c6e0299a5cb551f645e4cb9c
 | 
			
		||||
MAIN_WALLET=46d3684932f300a7fcdc8cc73cfa3057b5f61695c6e0299a5cb551f645e4cb9
 | 
			
		||||
PUB_KEY=0x1a63208e5b82588320a8C24b2c595Ba5d5cbfF3
 | 
			
		||||
							
								
								
									
										0
									
								
								app/blockchain/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								app/blockchain/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										17
									
								
								app/blockchain/api/serializers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/blockchain/api/serializers.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
from django.core.validators import MinValueValidator
 | 
			
		||||
from rest_framework import serializers
 | 
			
		||||
from rest_framework.generics import get_object_or_404
 | 
			
		||||
 | 
			
		||||
from users.models import User
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TransactFromAdminSerializer(serializers.Serializer):
 | 
			
		||||
    username = serializers.CharField(max_length=200)
 | 
			
		||||
    amount = serializers.IntegerField(validators=[MinValueValidator(1)])
 | 
			
		||||
 | 
			
		||||
    def validate_username(self, val):
 | 
			
		||||
        return get_object_or_404(User, username=val)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TransactToAdminSerializer(serializers.Serializer):
 | 
			
		||||
    amount = serializers.IntegerField(validators=[MinValueValidator(1)])
 | 
			
		||||
							
								
								
									
										76
									
								
								app/blockchain/api/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								app/blockchain/api/views.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,76 @@
 | 
			
		|||
from drf_yasg.utils import swagger_auto_schema
 | 
			
		||||
from rest_framework.permissions import IsAuthenticated
 | 
			
		||||
from rest_framework.views import APIView
 | 
			
		||||
from rest_framework.response import Response
 | 
			
		||||
 | 
			
		||||
from blockchain.api.serializers import (
 | 
			
		||||
    TransactFromAdminSerializer,
 | 
			
		||||
    TransactToAdminSerializer,
 | 
			
		||||
)
 | 
			
		||||
from blockchain.models import Transaction
 | 
			
		||||
from blockchain.services import (
 | 
			
		||||
    transact_from_admin,
 | 
			
		||||
    transact_to_admin,
 | 
			
		||||
    list_user_transactions,
 | 
			
		||||
)
 | 
			
		||||
from common.permissions import IsAdmin
 | 
			
		||||
from utils.blockchain import get_balance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TransactFromAdminView(APIView):
 | 
			
		||||
    permission_classes = [IsAuthenticated, IsAdmin]
 | 
			
		||||
 | 
			
		||||
    @swagger_auto_schema(request_body=TransactFromAdminSerializer)
 | 
			
		||||
    def post(self, request):
 | 
			
		||||
        serializer = TransactFromAdminSerializer(data=request.data)
 | 
			
		||||
        serializer.is_valid(raise_exception=True)
 | 
			
		||||
        user = serializer.validated_data["username"]
 | 
			
		||||
        amount = serializer.validated_data["amount"]
 | 
			
		||||
        transact_from_admin(user, amount)
 | 
			
		||||
        return Response({"amount": user.money})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TransactToAdminView(APIView):
 | 
			
		||||
    permission_classes = [IsAuthenticated]
 | 
			
		||||
 | 
			
		||||
    @swagger_auto_schema(request_body=TransactToAdminSerializer)
 | 
			
		||||
    def post(self, request):
 | 
			
		||||
        serializer = TransactToAdminSerializer(data=request.data)
 | 
			
		||||
        serializer.is_valid(raise_exception=True)
 | 
			
		||||
        amount = serializer.validated_data["amount"]
 | 
			
		||||
        transact_to_admin(request.user, amount)
 | 
			
		||||
        return Response({"amount": request.user.money})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TransactToUserView(APIView):
 | 
			
		||||
    permission_classes = [IsAuthenticated]
 | 
			
		||||
 | 
			
		||||
    @swagger_auto_schema(request_body=TransactFromAdminSerializer)
 | 
			
		||||
    def post(self, request):
 | 
			
		||||
        serializer = TransactFromAdminSerializer(data=request.data)
 | 
			
		||||
        serializer.is_valid(raise_exception=True)
 | 
			
		||||
        user = serializer.validated_data["username"]
 | 
			
		||||
        amount = serializer.validated_data["amount"]
 | 
			
		||||
        t = Transaction.objects.create(
 | 
			
		||||
            user_from=request.user, user_to=user, amount=amount
 | 
			
		||||
        )
 | 
			
		||||
        return Response({"your_amount": t.user_from.money, "amount": t.user_to.money})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GetMoneyApi(APIView):
 | 
			
		||||
    permission_classes = [IsAuthenticated]
 | 
			
		||||
 | 
			
		||||
    def get(self, request):
 | 
			
		||||
        user_money = int(get_balance(request.user.wallet_public_key).coins)
 | 
			
		||||
        if request.user.money != user_money:
 | 
			
		||||
            request.user.money = user_money
 | 
			
		||||
            request.user.save(update_fields=["money"])
 | 
			
		||||
 | 
			
		||||
        return Response({"amount": user_money})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TransactionHistoryApi(APIView):
 | 
			
		||||
    permission_classes = [IsAuthenticated]
 | 
			
		||||
 | 
			
		||||
    def get(self, request):
 | 
			
		||||
        return Response(list_user_transactions(request.user))
 | 
			
		||||
| 
						 | 
				
			
			@ -2,5 +2,8 @@ from django.apps import AppConfig
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class BlockchainConfig(AppConfig):
 | 
			
		||||
    default_auto_field = 'django.db.models.BigAutoField'
 | 
			
		||||
    name = 'blockchain'
 | 
			
		||||
    default_auto_field = "django.db.models.BigAutoField"
 | 
			
		||||
    name = "blockchain"
 | 
			
		||||
 | 
			
		||||
    def ready(self):
 | 
			
		||||
        import blockchain.signals
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,18 @@ class Transaction(models.Model):
 | 
			
		|||
 | 
			
		||||
    hash = models.CharField(max_length=256, unique=True)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def t_type(self):
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def user_from_username(self):
 | 
			
		||||
        return self.user_from.username
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def user_to_username(self):
 | 
			
		||||
        return self.user_to.username
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return f"transaction from {self.user_from} to {self.user_to}"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -39,11 +51,15 @@ class AdminTransaction(models.Model):
 | 
			
		|||
    hash = models.CharField(max_length=256, unique=True)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def user_from(self):
 | 
			
		||||
    def user_from_username(self):
 | 
			
		||||
        return "system" if self.type == self.TransactionType.TO else self.user.username
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def user_to(self):
 | 
			
		||||
    def t_type(self):
 | 
			
		||||
        return self.type.lower()
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def user_to_username(self):
 | 
			
		||||
        return (
 | 
			
		||||
            "system" if self.type == self.TransactionType.FROM else self.user.username
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,25 +1,33 @@
 | 
			
		|||
from itertools import chain
 | 
			
		||||
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from rest_framework.exceptions import ValidationError
 | 
			
		||||
 | 
			
		||||
from blockchain.models import Transaction, AdminTransaction
 | 
			
		||||
from users.models import User
 | 
			
		||||
from utils.blockchain import transfer_rubbles
 | 
			
		||||
from utils.blockchain import transfer_rubbles, get_balance
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def list_user_transactions(user: User) -> list:
 | 
			
		||||
    transaction = []
 | 
			
		||||
    qs = (
 | 
			
		||||
        user.transactions_to.all()
 | 
			
		||||
        | user.transactions_from.all()
 | 
			
		||||
        | user.admin_transactions.all()
 | 
			
		||||
    l = (
 | 
			
		||||
        [x for x in user.transactions_to.all()]
 | 
			
		||||
        + [x for x in user.transactions_from.all()]
 | 
			
		||||
        + [x for x in user.admin_transactions.all()]
 | 
			
		||||
    )
 | 
			
		||||
    l.sort(
 | 
			
		||||
        key=lambda instance: instance.created, reverse=True
 | 
			
		||||
    )
 | 
			
		||||
    qs.order_by("created")
 | 
			
		||||
    username = user.username
 | 
			
		||||
    for el in qs:  # type: Transaction
 | 
			
		||||
    for el in l:  # type: Transaction
 | 
			
		||||
        if not (t := el.t_type):
 | 
			
		||||
            t = "to" if el.user_from.username == username else "from"
 | 
			
		||||
 | 
			
		||||
        transaction.append(
 | 
			
		||||
            {
 | 
			
		||||
                "type": "addition" if el.user_from.username == username else "transfer",
 | 
			
		||||
                "user_from": el.user_from.username,
 | 
			
		||||
                "user_to": el.user_to.username,
 | 
			
		||||
                "type": t,
 | 
			
		||||
                "user_from": el.user_from_username,
 | 
			
		||||
                "user_to": el.user_to_username,
 | 
			
		||||
                "amount": el.amount,
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			@ -36,3 +44,21 @@ def transact_from_admin(user: User, amount: int):
 | 
			
		|||
    user.money += amount
 | 
			
		||||
    user.save(update_fields=["money"])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def transact_to_admin(user: User, amount: int):
 | 
			
		||||
    user_from_money = int(get_balance(user.wallet_public_key).coins)
 | 
			
		||||
    if user.money != user_from_money:
 | 
			
		||||
        user.money = user_from_money
 | 
			
		||||
        user.save(update_fields=["money"])
 | 
			
		||||
 | 
			
		||||
    if user_from_money - amount < 0:
 | 
			
		||||
        raise ValidationError("Not enough money")
 | 
			
		||||
 | 
			
		||||
    t = transfer_rubbles(user.wallet_private_key, settings.PUB_KEY, amount)
 | 
			
		||||
    print(t.transaction_hash)
 | 
			
		||||
    user.money -= amount
 | 
			
		||||
    user.save(update_fields=["money"])
 | 
			
		||||
 | 
			
		||||
    AdminTransaction.objects.create(
 | 
			
		||||
        type="PAYMENT", amount=amount, user=user, hash=t.transaction_hash
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,16 +21,18 @@ def validate_create_transaction(sender, instance: Transaction, **kwargs):
 | 
			
		|||
        instance.user_from.money = user_from_money
 | 
			
		||||
        instance.user_from.save(update_fields=["money"])
 | 
			
		||||
 | 
			
		||||
    if user_from_money - instance.amount <= 0:
 | 
			
		||||
    if user_from_money - instance.amount < 0:
 | 
			
		||||
        raise ValidationError(
 | 
			
		||||
            f"{instance.user_from.username} doesn't have enough money"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    transaction = transfer_rubbles(
 | 
			
		||||
        instance.user_from.wallet_private_key,
 | 
			
		||||
        instance.user_to.wallet_public_key,
 | 
			
		||||
        amount=instance.amount,
 | 
			
		||||
    )
 | 
			
		||||
    try:
 | 
			
		||||
        transaction = transfer_rubbles(
 | 
			
		||||
            instance.user_from.wallet_private_key,
 | 
			
		||||
            instance.user_to.wallet_public_key,
 | 
			
		||||
            amount=instance.amount,
 | 
			
		||||
        )
 | 
			
		||||
    except AttributeError:
 | 
			
		||||
        raise ValidationError("Transaction unsuccessful")
 | 
			
		||||
    instance.user_from.money -= instance.amount
 | 
			
		||||
    instance.user_from.save(update_fields=["money"])
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,12 @@
 | 
			
		|||
from django.urls import path, include
 | 
			
		||||
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
 | 
			
		||||
 | 
			
		||||
from blockchain.api.views import (
 | 
			
		||||
    TransactFromAdminView,
 | 
			
		||||
    TransactToUserView,
 | 
			
		||||
    TransactToAdminView,
 | 
			
		||||
    GetMoneyApi, TransactionHistoryApi,
 | 
			
		||||
)
 | 
			
		||||
from events.api.views import (
 | 
			
		||||
    ListCreateEventApi,
 | 
			
		||||
    RetrieveUpdateDeleteEventApi,
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +39,38 @@ urlpatterns = [
 | 
			
		|||
            ]
 | 
			
		||||
        ),
 | 
			
		||||
    ),
 | 
			
		||||
    path(
 | 
			
		||||
        "blockchain/",
 | 
			
		||||
        include(
 | 
			
		||||
            [
 | 
			
		||||
                path(
 | 
			
		||||
                    "salary/",
 | 
			
		||||
                    TransactFromAdminView.as_view(),
 | 
			
		||||
                    name="admin_to_user_transaction_api",
 | 
			
		||||
                ),
 | 
			
		||||
                path(
 | 
			
		||||
                    "payment/",
 | 
			
		||||
                    TransactToAdminView.as_view(),
 | 
			
		||||
                    name="user_to_admin_transaction_api",
 | 
			
		||||
                ),
 | 
			
		||||
                path(
 | 
			
		||||
                    "transact/",
 | 
			
		||||
                    TransactToUserView.as_view(),
 | 
			
		||||
                    name="user_to_user_transaction_api",
 | 
			
		||||
                ),
 | 
			
		||||
                path(
 | 
			
		||||
                    "amount/",
 | 
			
		||||
                    GetMoneyApi.as_view(),
 | 
			
		||||
                    name="get_user_money_api",
 | 
			
		||||
                ),
 | 
			
		||||
                path(
 | 
			
		||||
                    "history/",
 | 
			
		||||
                    TransactionHistoryApi.as_view(),
 | 
			
		||||
                    name="list_transactions_api",
 | 
			
		||||
                ),
 | 
			
		||||
            ]
 | 
			
		||||
        ),
 | 
			
		||||
    ),
 | 
			
		||||
    path(
 | 
			
		||||
        "events/",
 | 
			
		||||
        include(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -219,4 +219,5 @@ CORS_ALLOW_ALL_ORIGINS = True
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
MAIN_WALLET = env("MAIN_WALLET")
 | 
			
		||||
PUB_KEY = env("PUB_KEY")
 | 
			
		||||
TELEGRAM_API = "https://tender-badgers-eat-178-71-165-37.loca.lt"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,11 @@ class User(AbstractUser):
 | 
			
		|||
        max_length=6, choices=WorkerType.choices, default=WorkerType.WORKER
 | 
			
		||||
    )
 | 
			
		||||
    clan = models.ForeignKey(
 | 
			
		||||
        "users.Clan", related_name="users", on_delete=models.SET_NULL, null=True
 | 
			
		||||
        "users.Clan",
 | 
			
		||||
        related_name="users",
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
        null=True,
 | 
			
		||||
        blank=True,
 | 
			
		||||
    )
 | 
			
		||||
    command = models.ForeignKey(
 | 
			
		||||
        "users.Command", related_name="workers", on_delete=models.CASCADE
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,9 @@
 | 
			
		|||
from django.db.models.signals import pre_save, post_save
 | 
			
		||||
from django.dispatch import receiver
 | 
			
		||||
 | 
			
		||||
from conf import settings
 | 
			
		||||
from users.models import User
 | 
			
		||||
from utils.blockchain import create_wallet
 | 
			
		||||
from utils.blockchain import create_wallet, send_matic
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@receiver(post_save, sender=User)
 | 
			
		||||
| 
						 | 
				
			
			@ -11,6 +12,7 @@ def process_user(sender, instance, created, **kwargs):
 | 
			
		|||
        instance.set_password(instance.password)
 | 
			
		||||
 | 
			
		||||
        wallet = create_wallet()
 | 
			
		||||
        send_matic(settings.MAIN_WALLET, wallet.publicKey, 0.1)
 | 
			
		||||
 | 
			
		||||
        instance.wallet_public_key = wallet.publicKey
 | 
			
		||||
        instance.wallet_private_key = wallet.privateKey
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -132,3 +132,20 @@ def get_balance(my_public_wallet_key: str) -> WalletBalance:
 | 
			
		|||
    return WalletBalance(
 | 
			
		||||
        matic=res.json()["maticAmount"], coins=res.json()["coinsAmount"]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def send_matic(
 | 
			
		||||
    my_private_wallet_key: str, transfer_publick_key: str, amount: float
 | 
			
		||||
) -> TransHash:
 | 
			
		||||
    res = r.post(
 | 
			
		||||
        URL + "/v1/transfers/matic",
 | 
			
		||||
        data=json.dumps(
 | 
			
		||||
            {
 | 
			
		||||
                "fromPrivateKey": my_private_wallet_key,
 | 
			
		||||
                "toPublicKey": transfer_publick_key,
 | 
			
		||||
                "amount": amount,
 | 
			
		||||
            }
 | 
			
		||||
        ),
 | 
			
		||||
        headers=base_headers,
 | 
			
		||||
    )
 | 
			
		||||
    return TransHash(transaction_hash=res.json()["transaction"])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user