Skip to content
Snippets Groups Projects
views.py 5.57 KiB
Newer Older
Patricio Kumagae's avatar
Patricio Kumagae committed
import time
import datetime
import hashlib
import base64
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from web3 import Web3
from web3.exceptions import CannotHandleRequest
from bitcoin.core.serialize import uint256_from_str
from raven.contrib.django.raven_compat.models import client

from TsaApi.settings import CURRENT_CONTRACT_VERSION, ACCOUNT_ADDRESS, CONTRACTS
Patricio Kumagae's avatar
Patricio Kumagae committed
from app.managers import TimestampManager

class Stamp(APIView):
    """
    [POST]
    Permite realizar un stamp de un archivo

    Parámetros recibidos JSON:
    - file_hash: El hash del archivo encodeado en sha256


    Devuelve un proof para poder verificar en el futuro que el archivo fue incluido a la Blockchain

    Ejemplo:
    {
      "file_hash": "1957db7fe23e4be1740ddeb941ddda7ae0a6b782e536a9e00b5aa82db1e84547"
    }
    """

    def post(self, request):

        try:
            if not request.data.get('file_hash'):
                raise ValidationError('file_hash')

            file_hash = request.data.get('file_hash')

            account_hash = hashlib.sha256(ACCOUNT_ADDRESS.encode('utf-8')).hexdigest()
            timestamp_hash = hashlib.sha256(str(int(time.time())).encode('utf-8')).hexdigest()
            proof_hash = hashlib.sha256(str(file_hash + timestamp_hash + account_hash).encode('utf-8')).hexdigest() + CURRENT_CONTRACT_VERSION

            blockchain_hash = hashlib.sha256(str(file_hash + proof_hash).encode('utf-8')).hexdigest()

            web3 = TimestampManager.get_provider()

            contract = web3.eth.contract(abi=CONTRACTS[CURRENT_CONTRACT_VERSION]['abi'], address=Web3.toChecksumAddress(CONTRACTS[CURRENT_CONTRACT_VERSION]['address']))
            tx_hash = contract.functions.put(uint256_from_str(blockchain_hash.encode('utf-8'))).transact({'from': Web3.toChecksumAddress(ACCOUNT_ADDRESS)})

            proof = proof_hash + '-' + tx_hash.hex()

            base64_proof = base64.b64encode(proof.encode('utf-8')).decode('utf-8')

            return Response({'status': _('success'), _('proof'): base64_proof}, status=status.HTTP_200_OK)

        except ValidationError:
            return Response({'status': _('failure'), _('messages'): _('parameter_missing')}, status=status.HTTP_400_BAD_REQUEST)
        except Exception:
            client.captureException()
            return Response({'status': _('failure'), _('messages'): _('operation_failed')}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class Verify(APIView):
    """
    [POST]
    Permite verificar que un archivo fue subido a la Blockchain

    Parámetros recibidos JSON:
    - file_hash: El hash del archivo original encodeado en sha256
    - proof: Proof recibido como prueba al momento de realizar el stamp

    Devuelve número de bloque, fecha y hora de subida a la Blockchain

    Ejemplo:
    {
      "file_hash": "1957db7fe23e4be1740ddeb941ddda7ae0a6b782e536a9e00b5aa82db1e84547",
      "proof": "NzNkYzA5OGJkODlmZjdlMjc4OGFjMzJlNmU2ODdiOTdmODdiMTBjMWIyNzg5OTFlMDNkN2E2YWVkMDk3ODJkZTAxLTB4NGM2ZmNiNDBhMmUyZGVjYzc2YWQzMjM3MDU2NzZjMjljYWE1MmIyYjZkMDdiMDIzYjBhY2EzOWYzZGIxYmRlZg=="
    }
    """
    def post(self, request):

        try:

            if not request.data.get('file_hash'):
                raise ValidationError('file_hash')

            if not request.data.get('proof'):
                raise ValidationError('proof')

            pending = False

            file_hash = request.data.get('file_hash')
            base64_proof = request.data.get('proof')

            proof = base64.b64decode(base64_proof).decode('utf-8')
            proof_hash, tx_hash = proof.split('-')
            contract_version = proof_hash[-2:]
            contract_info = CONTRACTS[contract_version]

            web3 = TimestampManager.get_provider()

            contract = web3.eth.contract(abi=contract_info['abi'], address=Web3.toChecksumAddress(contract_info['address']))

            hash = hashlib.sha256(str(file_hash+proof_hash).encode('utf-8')).hexdigest()

            block_number = contract.functions.get(uint256_from_str(hash.encode('utf-8'))).call()

            if block_number == 0:
                try:
                    transaction = web3.eth.getTransaction(tx_hash)
                    if transaction and not transaction.blockNumber:
                        pending = True
                except ValueError:
                    pass

                if pending:
                    return Response({'status': _('success'), _('messages'): _('transaction_pending')},status=status.HTTP_200_OK)
                else:
                    return Response({'status': _('failure'), _('messages'): _('file_not_found')}, status=status.HTTP_404_NOT_FOUND)

            block = web3.eth.getBlock(block_number)

            date = datetime.datetime.fromtimestamp(block.timestamp).strftime('%d/%m/%Y %H:%M:%S')

            return Response({'status': _('success'), _('messages'): _('file_uploaded') % (file_hash, str(block.number), str(date)) }, status=status.HTTP_200_OK)

        except ValidationError as e:
            return Response({'status': _('failure'), _('messages'): _('parameter_missing') % e.message}, status=status.HTTP_400_BAD_REQUEST)
        except CannotHandleRequest:
            return Response({'status': _('failure'), _('messages'): _('could_not_connect')}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
        except Exception:
            client.captureException()
            return Response({'status': _('failure'), _('messages'): _('operation_failed')}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)