fixed blockchain, added base operations

This commit is contained in:
Alexander Karpov 2022-10-09 04:33:12 +03:00
parent 79587c322d
commit abbd90551a
13 changed files with 227 additions and 24 deletions

View File

@ -1,4 +1,5 @@
DJANGO_DEBUG=yes DJANGO_DEBUG=yes
DATABASE_URL=postgres://postgres:debug@127.0.0.1:5432/moretech DATABASE_URL=postgres://postgres:debug@127.0.0.1:5432/moretech
CELERY_BROKER_URL=redis://localhost:6379/0 CELERY_BROKER_URL=redis://localhost:6379/0
MAIN_WALLET=46d3684932f300a7fcdc8cc73cfa3057b5f61695c6e0299a5cb551f645e4cb9c MAIN_WALLET=46d3684932f300a7fcdc8cc73cfa3057b5f61695c6e0299a5cb551f645e4cb9
PUB_KEY=0x1a63208e5b82588320a8C24b2c595Ba5d5cbfF3

View File

View 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)])

View 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))

View File

@ -2,5 +2,8 @@ from django.apps import AppConfig
class BlockchainConfig(AppConfig): class BlockchainConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = "django.db.models.BigAutoField"
name = 'blockchain' name = "blockchain"
def ready(self):
import blockchain.signals

View File

@ -19,6 +19,18 @@ class Transaction(models.Model):
hash = models.CharField(max_length=256, unique=True) 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): def __str__(self):
return f"transaction from {self.user_from} to {self.user_to}" 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) hash = models.CharField(max_length=256, unique=True)
@property @property
def user_from(self): def user_from_username(self):
return "system" if self.type == self.TransactionType.TO else self.user.username return "system" if self.type == self.TransactionType.TO else self.user.username
@property @property
def user_to(self): def t_type(self):
return self.type.lower()
@property
def user_to_username(self):
return ( return (
"system" if self.type == self.TransactionType.FROM else self.user.username "system" if self.type == self.TransactionType.FROM else self.user.username
) )

View File

@ -1,25 +1,33 @@
from itertools import chain
from django.conf import settings from django.conf import settings
from rest_framework.exceptions import ValidationError
from blockchain.models import Transaction, AdminTransaction from blockchain.models import Transaction, AdminTransaction
from users.models import User 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: def list_user_transactions(user: User) -> list:
transaction = [] transaction = []
qs = ( l = (
user.transactions_to.all() [x for x in user.transactions_to.all()]
| user.transactions_from.all() + [x for x in user.transactions_from.all()]
| user.admin_transactions.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 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( transaction.append(
{ {
"type": "addition" if el.user_from.username == username else "transfer", "type": t,
"user_from": el.user_from.username, "user_from": el.user_from_username,
"user_to": el.user_to.username, "user_to": el.user_to_username,
"amount": el.amount, "amount": el.amount,
} }
) )
@ -36,3 +44,21 @@ def transact_from_admin(user: User, amount: int):
user.money += amount user.money += amount
user.save(update_fields=["money"]) 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
)

View File

@ -21,16 +21,18 @@ def validate_create_transaction(sender, instance: Transaction, **kwargs):
instance.user_from.money = user_from_money instance.user_from.money = user_from_money
instance.user_from.save(update_fields=["money"]) instance.user_from.save(update_fields=["money"])
if user_from_money - instance.amount <= 0: if user_from_money - instance.amount < 0:
raise ValidationError( raise ValidationError(
f"{instance.user_from.username} doesn't have enough money" f"{instance.user_from.username} doesn't have enough money"
) )
try:
transaction = transfer_rubbles( transaction = transfer_rubbles(
instance.user_from.wallet_private_key, instance.user_from.wallet_private_key,
instance.user_to.wallet_public_key, instance.user_to.wallet_public_key,
amount=instance.amount, amount=instance.amount,
) )
except AttributeError:
raise ValidationError("Transaction unsuccessful")
instance.user_from.money -= instance.amount instance.user_from.money -= instance.amount
instance.user_from.save(update_fields=["money"]) instance.user_from.save(update_fields=["money"])

View File

@ -1,6 +1,12 @@
from django.urls import path, include from django.urls import path, include
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from blockchain.api.views import (
TransactFromAdminView,
TransactToUserView,
TransactToAdminView,
GetMoneyApi, TransactionHistoryApi,
)
from events.api.views import ( from events.api.views import (
ListCreateEventApi, ListCreateEventApi,
RetrieveUpdateDeleteEventApi, 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( path(
"events/", "events/",
include( include(

View File

@ -219,4 +219,5 @@ CORS_ALLOW_ALL_ORIGINS = True
MAIN_WALLET = env("MAIN_WALLET") MAIN_WALLET = env("MAIN_WALLET")
PUB_KEY = env("PUB_KEY")
TELEGRAM_API = "https://tender-badgers-eat-178-71-165-37.loca.lt" TELEGRAM_API = "https://tender-badgers-eat-178-71-165-37.loca.lt"

View File

@ -23,7 +23,11 @@ class User(AbstractUser):
max_length=6, choices=WorkerType.choices, default=WorkerType.WORKER max_length=6, choices=WorkerType.choices, default=WorkerType.WORKER
) )
clan = models.ForeignKey( 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( command = models.ForeignKey(
"users.Command", related_name="workers", on_delete=models.CASCADE "users.Command", related_name="workers", on_delete=models.CASCADE

View File

@ -1,8 +1,9 @@
from django.db.models.signals import pre_save, post_save from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver from django.dispatch import receiver
from conf import settings
from users.models import User from users.models import User
from utils.blockchain import create_wallet from utils.blockchain import create_wallet, send_matic
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
@ -11,6 +12,7 @@ def process_user(sender, instance, created, **kwargs):
instance.set_password(instance.password) instance.set_password(instance.password)
wallet = create_wallet() wallet = create_wallet()
send_matic(settings.MAIN_WALLET, wallet.publicKey, 0.1)
instance.wallet_public_key = wallet.publicKey instance.wallet_public_key = wallet.publicKey
instance.wallet_private_key = wallet.privateKey instance.wallet_private_key = wallet.privateKey

View File

@ -132,3 +132,20 @@ def get_balance(my_public_wallet_key: str) -> WalletBalance:
return WalletBalance( return WalletBalance(
matic=res.json()["maticAmount"], coins=res.json()["coinsAmount"] 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"])