Skip to content
Snippets Groups Projects
Commit 2ee7ca47 authored by Patricio Kumagae's avatar Patricio Kumagae
Browse files

Subida inicial TsaApi

parents
No related branches found
No related tags found
No related merge requests found
Showing with 492 additions and 0 deletions
local_settings_dev.py
\ No newline at end of file
ACCOUNT_ADDRESS = '0x21ea59FC5cE54a827E20BC9b736FeeD8F9C880Ff'
HOST_ADDRESS = 'http://10.23.10.71:8501'
#Contracts info
CONTRACTS = {
'01':{
'address': '0x5B063742464f4c81EAE6B597DceaEe619F46d5aE',
'abi': [{"constant":False,"inputs":[{"name":"hash","type":"uint256"}],"name":"put","outputs":[],"payable":False,"stateMutability":"nonpayable","type":"function"},{"constant":True,"inputs":[{"name":"hash","type":"uint256"}],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":False,"stateMutability":"view","type":"function"}] ,
},
}
CURRENT_CONTRACT_VERSION = '01'
\ No newline at end of file
ACCOUNT_ADDRESS = '0x21ea59FC5cE54a827E20BC9b736FeeD8F9C880Ff'
HOST_ADDRESS = 'http://10.23.10.71:8501'
#Contracts info
CONTRACTS = {
'01':{
'address': '0x5B063742464f4c81EAE6B597DceaEe619F46d5aE',
'abi': [{"constant":False,"inputs":[{"name":"hash","type":"uint256"}],"name":"put","outputs":[],"payable":False,"stateMutability":"nonpayable","type":"function"},{"constant":True,"inputs":[{"name":"hash","type":"uint256"}],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":False,"stateMutability":"view","type":"function"}] ,
},
}
CURRENT_CONTRACT_VERSION = '01'
\ No newline at end of file
ACCOUNT_ADDRESS = '0x21ea59FC5cE54a827E20BC9b736FeeD8F9C880Ff'
HOST_ADDRESS = 'http://10.23.10.71:8501'
#Contracts info
CONTRACTS = {
'01':{
'address': '0x5B063742464f4c81EAE6B597DceaEe619F46d5aE',
'abi': [{"constant":False,"inputs":[{"name":"hash","type":"uint256"}],"name":"put","outputs":[],"payable":False,"stateMutability":"nonpayable","type":"function"},{"constant":True,"inputs":[{"name":"hash","type":"uint256"}],"name":"get","outputs":[{"name":"","type":"uint256"}],"payable":False,"stateMutability":"view","type":"function"}] ,
},
}
CURRENT_CONTRACT_VERSION = '01'
\ No newline at end of file
"""
Django settings for TsaApi project.
Generated by 'django-admin startproject' using Django 1.11.7.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""
import os
import datetime
import raven
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'k-9nuqgkzn64pi4^ex)syxv7_$gg1p7k1bfaunkgk13c5*ib=('
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'raven.contrib.django.raven_compat',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'TsaApi.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'TsaApi.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/
LANGUAGE_CODE = 'es'
from django.utils.translation import ugettext_lazy as _
LANGUAGES = (
('es', _('Spanish')),
)
LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale'),
)
TIME_ZONE = 'America/Argentina/Buenos_Aires'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/
STATIC_URL = '/static/'
# Habilitar para permitir la autenticación
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
# 'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
# 'rest_framework.authentication.SessionAuthentication',
# 'rest_framework.authentication.BasicAuthentication',
),
}
SENTRY_URL = 'http://2ec54beaab3b4459a1e5afadea070f98:92aee3f5d91e4e66996a3e0792985541@172.17.30.21:9000/54'
RAVEN_CONFIG = {
'dsn': SENTRY_URL,
}
try:
from TsaApi.local_settings import *
except ImportError as e:
pass
from django.conf.urls import url
from django.contrib import admin
from rest_framework_jwt.views import obtain_jwt_token
from django.urls import include
urlpatterns = [
url(r'api/tsa/', include('app.urls')),
url(r'^api-token-auth/', obtain_jwt_token),
]
"""
WSGI config for TsaApi project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "TsaApi.settings")
application = get_wsgi_application()
from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class AppConfig(AppConfig):
name = 'app'
class EmptyParameterException(Exception):
pass
from django.db import models
from web3 import Web3, HTTPProvider
from web3.exceptions import CannotHandleRequest, UnhandledRequest
from TsaApi.settings import HOST_ADDRESS
from web3.middleware import geth_poa_middleware
class TimestampManager(models.Manager):
@classmethod
def get_provider(cls):
try:
web3 = Web3(HTTPProvider(HOST_ADDRESS))
web3.middleware_stack.inject(geth_poa_middleware, layer=0)
if not web3.isConnected():
raise CannotHandleRequest
return web3
except UnhandledRequest:
raise
from django.db import models
class Timestamp(models.Model):
id = models.IntegerField(primary_key=True)
file_hash = models.CharField(max_length=64)
incomplete_timestamp = models.BinaryField()
complete_timestamp = models.BinaryField()
class Meta:
db_table = 'timestamp'
from django.test import TestCase
# Create your tests here.
from django.conf.urls import url
from . import views
urlpatterns = [
#path('', views.index, name='index'),
url(r'stamp/', views.Stamp.as_view()),
url(r'verify/', views.Verify.as_view()),
]
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, ACCOUNT_PASSWORD, CONTRACTS
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)
File added
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-13 11:02-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: TsaApi/settings.py:111
msgid "Spanish"
msgstr ""
#: app/views.py:60 app/views.py:126 app/views.py:134
msgid "success"
msgstr ""
#: app/views.py:60
msgid "proof"
msgstr ""
#: app/views.py:63 app/views.py:65 app/views.py:128 app/views.py:137
#: app/views.py:139 app/views.py:141
msgid "failure"
msgstr ""
#: app/views.py:63 app/views.py:65 app/views.py:126 app/views.py:128
#: app/views.py:134 app/views.py:137 app/views.py:139 app/views.py:141
msgid "messages"
msgstr ""
#: app/views.py:63 app/views.py:137
msgid "parameter_missing"
msgstr ""
#: app/views.py:65 app/views.py:141
msgid "operation_failed"
msgstr ""
#: app/views.py:126
msgid "transaction_pending"
msgstr ""
#: app/views.py:128
msgid "file_not_found"
msgstr ""
#: app/views.py:134
msgid "file_uploaded"
msgstr ""
#: app/views.py:139
msgid "could_not_connect"
msgstr ""
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment