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

Merge branch 'desarrollo'

# Conflicts:
#	TsaApi/settings.py
parents 862fe9d5 d1c47713
No related branches found
No related tags found
No related merge requests found
......@@ -13,3 +13,4 @@ TsaAPI fue diseñada para enviar archivos a la blockchain y poder verificar su e
pip install -r requirements.txt
```
- Crear un link simbólico al archivo local_settings.py desde el local settings del entorno en que se encuentre el proyecto
- Instalar libssl-dev build-essential automake pkg-config libtool libffi-dev libgmp-dev libyaml-cpp-dev
APP_ROOT = '/home/python/TsaAPI/'
STATIC_ROOT = APP_ROOT + 'html/'
ACCOUNT_ADDRESS = '0x21ea59FC5cE54a827E20BC9b736FeeD8F9C880Ff'
HOST_ADDRESS = 'http://10.23.10.71:8501'
#Contracts info
# CONTRACTS = {
# '01':{
# 'address': '0x98e3b125f6FeA352c9f5F1594dBB241aC2E483DE',
# 'abi': [{"constant":False,"inputs":[{"name":"ots","type":"uint256"},{"name":"file_hash","type":"uint256"}],"name":"stamp","outputs":[],"payable":False,"stateMutability":"nonpayable","type":"function"},{"constant":True,"inputs":[{"name":"ots","type":"uint256"},{"name":"file_hash","type":"uint256"}],"name":"verify","outputs":[{"name":"","type":"bool"}],"payable":False,"stateMutability":"view","type":"function"},{"constant":True,"inputs":[{"name":"ots","type":"uint256"}],"name":"getBlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":False,"stateMutability":"view","type":"function"},{"constant":True,"inputs":[{"name":"ots","type":"uint256"}],"name":"getHash","outputs":[{"name":"","type":"uint256"}],"payable":False,"stateMutability":"view","type":"function"}] ,
# },
# }
CONTRACTS = {
'01':{
'address': '0x98e3b125f6FeA352c9f5F1594dBB241aC2E483DE',
'abi': [{"constant":False,"inputs":[{"name":"ots","type":"uint256"},{"name":"file_hash","type":"uint256"}],"name":"stamp","outputs":[],"payable":False,"stateMutability":"nonpayable","type":"function"},{"constant":True,"inputs":[{"name":"ots","type":"uint256"},{"name":"file_hash","type":"uint256"}],"name":"verify","outputs":[{"name":"","type":"bool"}],"payable":False,"stateMutability":"view","type":"function"},{"constant":True,"inputs":[{"name":"ots","type":"uint256"}],"name":"getBlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":False,"stateMutability":"view","type":"function"},{"constant":True,"inputs":[{"name":"ots","type":"uint256"}],"name":"getHash","outputs":[{"name":"","type":"uint256"}],"payable":False,"stateMutability":"view","type":"function"}] ,
'address': '0x88933138A9Ef6474ee4f255A6395210F4f014d6B',
'abi': [{"constant":True,"inputs":[{"name":"ots","type":"string"}],"name":"getHash","outputs":[{"name":"","type":"string"}],"payable":False,"stateMutability":"view","type":"function"},{"constant":True,"inputs":[{"name":"ots","type":"string"}],"name":"getBlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":False,"stateMutability":"view","type":"function"},{"constant":True,"inputs":[{"name":"ots","type":"string"},{"name":"file_hash","type":"string"}],"name":"verify","outputs":[{"name":"","type":"bool"}],"payable":False,"stateMutability":"view","type":"function"},{"constant":False,"inputs":[{"name":"ots","type":"string"},{"name":"file_hash","type":"string"}],"name":"stamp","outputs":[],"payable":False,"stateMutability":"nonpayable","type":"function"}],
},
}
CURRENT_CONTRACT_VERSION = '01'
DEBUG = True
\ No newline at end of file
DEBUG = True
TEMPORARY_OTS_PREFIX = '0x'
PERMANENT_OTS_PREFIX = '1x'
\ No newline at end of file
APP_ROOT = '/home/python/TsaAPI/'
STATIC_ROOT = APP_ROOT + 'html/'
ACCOUNT_ADDRESS = '0x21ea59FC5cE54a827E20BC9b736FeeD8F9C880Ff'
......@@ -13,4 +16,7 @@ CONTRACTS = {
CURRENT_CONTRACT_VERSION = '01'
DEBUG = True
\ No newline at end of file
DEBUG = True
TEMPORARY_OTS_PREFIX = '0x'
PERMANENT_OTS_PREFIX = '1x'
\ No newline at end of file
APP_ROOT = '/home/python/TsaAPI/'
STATIC_ROOT = APP_ROOT + 'html/'
ACCOUNT_ADDRESS = '0x21ea59FC5cE54a827E20BC9b736FeeD8F9C880Ff'
......@@ -13,4 +16,7 @@ CONTRACTS = {
CURRENT_CONTRACT_VERSION = '01'
DEBUG = False
\ No newline at end of file
DEBUG = False
TEMPORARY_OTS_PREFIX = '0x'
PERMANENT_OTS_PREFIX = '1x'
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Django settings for TsaApi project.
......@@ -43,6 +44,7 @@ INSTALLED_APPS = [
'rest_framework',
'raven.contrib.django.raven_compat',
'corsheaders',
'rest_framework_swagger',
]
MIDDLEWARE = [
......@@ -137,7 +139,7 @@ USE_TZ = True
STATIC_URL = '/static/'
# Habilitar para permitir la autenticacion
# Habilitar para permitir la autenticación
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
# 'rest_framework.permissions.IsAuthenticated',
......@@ -150,6 +152,7 @@ REST_FRAMEWORK = {
}
SENTRY_URL = 'http://2ec54beaab3b4459a1e5afadea070f98:92aee3f5d91e4e66996a3e0792985541@172.17.30.21:9000/54'
RAVEN_CONFIG = {
'dsn': SENTRY_URL,
}
......
......@@ -5,7 +5,7 @@ from web3 import Web3, HTTPProvider
from web3.exceptions import CannotHandleRequest, UnhandledRequest
from TsaApi.settings import HOST_ADDRESS
from web3.middleware import geth_poa_middleware
from bitcoin.core.serialize import uint256_from_str
class TimestampManager(models.Manager):
......@@ -49,18 +49,18 @@ class TimestampManager(models.Manager):
return web3.eth.getTransaction(tx_hash)
@staticmethod
def stamp(proof_hash, file_hash):
def stamp(ots_hash, file_hash):
contract = TimestampManager.get_current_contract()
return contract.functions.stamp(uint256_from_str(proof_hash.encode('utf-8')), uint256_from_str(file_hash.encode('utf-8'))).transact({'from': Web3.toChecksumAddress(ACCOUNT_ADDRESS)})
return contract.functions.stamp(ots_hash, file_hash).transact({'from': Web3.toChecksumAddress(ACCOUNT_ADDRESS)})
@staticmethod
def verify(contract_version, proof_hash, file_hash):
def verify(contract_version, ots_hash, file_hash):
contract = TimestampManager.get_contract(contract_version)
return contract.functions.verify(uint256_from_str(proof_hash.encode('utf-8')),uint256_from_str(file_hash.encode('utf-8'))).call()
return contract.functions.verify(ots_hash, file_hash).call()
@staticmethod
def get_block_number(contract_version, proof_hash):
def get_block_number(contract_version, ots_hash):
contract = TimestampManager.get_contract(contract_version)
return contract.functions.getBlockNumber(uint256_from_str(proof_hash.encode('utf-8'))).call()
return contract.functions.getBlockNumber(ots_hash).call()
from django.conf.urls import url
from . import views
from rest_framework_swagger.views import get_swagger_view
schema_view = get_swagger_view(title='TSA API')
urlpatterns = [
#path('', views.index, name='index'),
url(r'stamp/', views.Stamp.as_view()),
url(r'verify/', views.Verify.as_view()),
url(r'docs/$', schema_view),
]
......
......@@ -2,6 +2,8 @@ import datetime
import hashlib
import time
from TsaApi.settings import ACCOUNT_ADDRESS, CURRENT_CONTRACT_VERSION
from ethereum.abi import (decode_abi, normalize_name as normalize_abi_method_name, method_id as get_abi_method_id)
from ethereum.utils import encode_int, zpad, decode_hex
class Utils():
......@@ -14,6 +16,24 @@ class Utils():
return datetime.datetime.fromtimestamp(timestamp).strftime('%d/%m/%Y %H:%M:%S')
@staticmethod
def get_proof_hash(file_hash):
#El proof se genera con los hashes sha256 de (archivo original+timestamp+dirección de la cuenta) + versión del contrato
def get_ots_hash(file_hash):
#El ots se genera con los hashes sha256 de (archivo original+timestamp+dirección de la cuenta) + versión del contrato
return Utils.sha256_encode(str(file_hash + Utils.sha256_encode(str(int(time.time()))) + Utils.sha256_encode(ACCOUNT_ADDRESS))) + CURRENT_CONTRACT_VERSION
@staticmethod
def decode_contract_call(contract_abi, call_data):
call_data_bin = decode_hex(call_data)
method_signature = call_data_bin[:4]
for description in contract_abi:
if description.get('type') != 'function':
continue
method_name = normalize_abi_method_name(description['name'])
arg_types = [item['type'] for item in description['inputs']]
method_id = get_abi_method_id(method_name, arg_types)
if zpad(encode_int(method_id), 4) == method_signature:
try:
args = decode_abi(arg_types, call_data_bin[4:])
except AssertionError:
# Invalid args
continue
return method_name, args
......@@ -6,9 +6,12 @@ from rest_framework.views import APIView
from rest_framework.response import Response
from web3.exceptions import CannotHandleRequest
from raven.contrib.django.raven_compat.models import client
from rest_framework.schemas import ManualSchema
import coreschema,coreapi
from app.managers import TimestampManager
from app.utils import Utils
from TsaApi.local_settings import TEMPORARY_OTS_PREFIX, PERMANENT_OTS_PREFIX, CONTRACTS
class Stamp(APIView):
"""
......@@ -19,8 +22,7 @@ class Stamp(APIView):
[Content-Type:application/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
Devuelve un OTS para poder verificar en el futuro que el archivo fue incluido a la Blockchain
Ejemplo:
{
......@@ -28,6 +30,18 @@ class Stamp(APIView):
}
"""
schema = ManualSchema(fields=[
coreapi.Field(
name='file_hash',
required=True,
location='form',
schema=coreschema.String(),
description='El hash del archivo encodeado en sha256',
),
])
def post(self, request):
try:
......@@ -36,20 +50,20 @@ class Stamp(APIView):
file_hash = request.data.get('file_hash')
proof_hash = Utils.get_proof_hash(file_hash)
ots_hash = Utils.get_ots_hash(file_hash)
tx_hash = TimestampManager.stamp(proof_hash, file_hash)
tx_hash = TimestampManager.stamp(ots_hash, file_hash)
#Al proof se le agrega la transacción para poder verificar luego si está pendiente de subida
proof = proof_hash + '-' + tx_hash.hex()
#Al OTS se le agrega la transacción para poder verificar luego si está pendiente de subida
ots = TEMPORARY_OTS_PREFIX + '-' + ots_hash + '-' + tx_hash.hex()
return Response({_('status'): _('success'), _('proof'): base64.b64encode(proof.encode('utf-8')).decode('utf-8')}, status=status.HTTP_200_OK)
return Response({_('status'): _('success'), _('temporary_ots'): base64.b64encode(ots.encode('utf-8')).decode('utf-8')}, 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:
except Exception as e:
client.captureException()
return Response({_('status'): _('failure'), _('messages'): _('operation_failed')}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
......@@ -62,54 +76,108 @@ class Verify(APIView):
Parámetros recibidos:
[Content-Type:application/json]
- file_hash: El hash del archivo original encodeado en sha256
- proof: Proof recibido como prueba al momento de realizar el stamp
- ots: OTS 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=="
"ots": "NzNkYzA5OGJkODlmZjdlMjc4OGFjMzJlNmU2ODdiOTdmODdiMTBjMWIyNzg5OTFlMDNkN2E2YWVkMDk3ODJkZTAxLTB4NGM2ZmNiNDBhMmUyZGVjYzc2YWQzMjM3MDU2NzZjMjljYWE1MmIyYjZkMDdiMDIzYjBhY2EzOWYzZGIxYmRlZg=="
}
"""
schema = ManualSchema(fields=[
coreapi.Field(
name='file_hash',
required=True,
location='form',
schema=coreschema.String(),
description='El hash del archivo encodeado en sha256',
), coreapi.Field(
name='ots',
required=True,
location='form',
schema=coreschema.String(),
description='El OTS recibido al hacer el stamp del archivo encodeado en sha256',
)
])
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')
if not request.data.get('ots'):
raise ValidationError('ots')
file_hash = request.data.get('file_hash')
base64_proof = request.data.get('proof')
base64_ots = request.data.get('ots')
ots = base64.b64decode(base64_ots).decode('utf-8')
ots_version = ots[:2]
if ots_version == PERMANENT_OTS_PREFIX:
ots_version, file_hash, ots_hash, tx_hash, block_number = ots.split('-')
transaction = TimestampManager.get_transaction(tx_hash)
method_name, args = Utils.decode_contract_call(CONTRACTS['01']['abi'], transaction.input)
proof = base64.b64decode(base64_proof).decode('utf-8')
proof_hash, tx_hash = proof.split('-')
contract_version = proof_hash[-2:]
tx_ots_hash = args[0].decode('utf-8')
tx_file_hash = args[1].decode('utf-8')
verified = TimestampManager.verify(contract_version, proof_hash, file_hash)
if tx_ots_hash == ots_hash and tx_file_hash == file_hash:
if verified:
block_number = TimestampManager.get_block_number(contract_version, proof_hash)
block = TimestampManager.get_block(block_number)
block = TimestampManager.get_block(int(block_number))
permanent_ots = PERMANENT_OTS_PREFIX + '-' + file_hash + '-' + ots_hash + '-' + tx_hash + '-' + str(block_number)
return Response({_('status'): _('success'),
_('permanent_ots'): base64.b64encode(permanent_ots.encode('utf-8')).decode('utf-8'),
_('messages'): _('file_uploaded') % (
file_hash, str(block.number), str(Utils.datetime_from_timestamp(block.timestamp)))},
status=status.HTTP_200_OK)
else:
return Response({_('status'): _('failure'), _('messages'): _('file_not_found')},
status=status.HTTP_404_NOT_FOUND)
return Response({_('status'): _('success'),_('messages'): _('file_uploaded') % (file_hash, str(block.number), str(Utils.datetime_from_timestamp(block.timestamp)))},status=status.HTTP_200_OK)
else:
try:
transaction = TimestampManager.get_transaction(tx_hash)
if transaction and not transaction.blockNumber:
return Response({_('status'): _('pending'), _('messages'): _('transaction_pending')}, status=status.HTTP_200_OK)
except ValueError:
pass
return Response({_('status'): _('failure'), _('messages'): _('file_not_found')},status=status.HTTP_404_NOT_FOUND)
ots_version, ots_hash, tx_hash = ots.split('-')
contract_version = ots_hash[-2:]
verified = TimestampManager.verify(contract_version, ots_hash, file_hash)
if verified:
block_number = TimestampManager.get_block_number(contract_version, ots_hash)
block = TimestampManager.get_block(block_number)
permanent_ots = PERMANENT_OTS_PREFIX + '-' + file_hash + '-' + ots_hash + '-' + tx_hash + '-' + str(block_number)
return Response({_('status'): _('success'), _('permanent_ots'): base64.b64encode(permanent_ots.encode('utf-8')).decode('utf-8') ,_('messages'): _('file_uploaded') % (file_hash, str(block.number), str(Utils.datetime_from_timestamp(block.timestamp)))},status=status.HTTP_200_OK)
else:
try:
transaction = TimestampManager.get_transaction(tx_hash)
if transaction and not transaction.blockNumber:
return Response({_('status'): _('pending'), _('messages'): _('transaction_pending')}, status=status.HTTP_200_OK)
except ValueError:
pass
return Response({_('status'): _('failure'), _('messages'): _('file_not_found')},status=status.HTTP_404_NOT_FOUND)
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:
except Exception as e:
client.captureException()
return Response({_('status'): _('failure'), _('messages'): _('operation_failed')}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
No preview for this file type
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-16 15:14-0300\n"
"POT-Creation-Date: 2018-08-30 15:08-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"
......@@ -17,59 +17,63 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: TsaApi/settings.py:114
#: TsaApi/settings.py:121
msgid "Spanish"
msgstr ""
#: app/views.py:45 app/views.py:48 app/views.py:50 app/views.py:53
#: app/views.py:97 app/views.py:102 app/views.py:106 app/views.py:109
#: app/views.py:111 app/views.py:114
#: app/views.py:60 app/views.py:63 app/views.py:65 app/views.py:68
#: app/views.py:144 app/views.py:150 app/views.py:171 app/views.py:176
#: app/views.py:180 app/views.py:185 app/views.py:187 app/views.py:190
msgid "status"
msgstr ""
#: app/views.py:45 app/views.py:97
#: app/views.py:60 app/views.py:144 app/views.py:171
msgid "success"
msgstr ""
#: app/views.py:45
msgid "proof"
#: app/views.py:60
msgid "temporary_ots"
msgstr ""
#: app/views.py:48 app/views.py:50 app/views.py:53 app/views.py:106
#: app/views.py:109 app/views.py:111 app/views.py:114
#: app/views.py:63 app/views.py:65 app/views.py:68 app/views.py:150
#: app/views.py:180 app/views.py:185 app/views.py:187 app/views.py:190
msgid "failure"
msgstr ""
#: app/views.py:48 app/views.py:50 app/views.py:53 app/views.py:97
#: app/views.py:102 app/views.py:106 app/views.py:109 app/views.py:111
#: app/views.py:114
#: app/views.py:63 app/views.py:65 app/views.py:68 app/views.py:146
#: app/views.py:150 app/views.py:171 app/views.py:176 app/views.py:180
#: app/views.py:185 app/views.py:187 app/views.py:190
msgid "messages"
msgstr ""
#: app/views.py:48 app/views.py:109
#: app/views.py:63 app/views.py:185
msgid "parameter_missing"
msgstr ""
#: app/views.py:50 app/views.py:111
#: app/views.py:65 app/views.py:187
msgid "could_not_connect"
msgstr ""
#: app/views.py:53 app/views.py:114
#: app/views.py:68 app/views.py:190
msgid "operation_failed"
msgstr ""
#: app/views.py:97
#: app/views.py:145 app/views.py:171
msgid "permanent_ots"
msgstr ""
#: app/views.py:146 app/views.py:171
msgid "file_uploaded"
msgstr ""
#: app/views.py:102
msgid "pending"
#: app/views.py:150 app/views.py:180
msgid "file_not_found"
msgstr ""
#: app/views.py:102
msgid "transaction_pending"
#: app/views.py:176
msgid "pending"
msgstr ""
#: app/views.py:106
msgid "file_not_found"
#: app/views.py:176
msgid "transaction_pending"
msgstr ""
No preview for this file type
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-16 15:14-0300\n"
"POT-Creation-Date: 2018-08-30 15:08-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"
......@@ -18,59 +18,66 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: TsaApi/settings.py:114
#: TsaApi/settings.py:121
msgid "Spanish"
msgstr ""
#: app/views.py:45 app/views.py:48 app/views.py:50 app/views.py:53
#: app/views.py:97 app/views.py:102 app/views.py:106 app/views.py:109
#: app/views.py:111 app/views.py:114
#: app/views.py:60 app/views.py:63 app/views.py:65 app/views.py:68
#: app/views.py:144 app/views.py:150 app/views.py:171 app/views.py:176
#: app/views.py:180 app/views.py:185 app/views.py:187 app/views.py:190
msgid "status"
msgstr "status"
#: app/views.py:45 app/views.py:97
#: app/views.py:60 app/views.py:144 app/views.py:171
msgid "success"
msgstr "success"
#: app/views.py:45
msgid "proof"
msgstr "proof"
#: app/views.py:60
msgid "temporary_ots"
msgstr "temporay_ots"
#: app/views.py:48 app/views.py:50 app/views.py:53 app/views.py:106
#: app/views.py:109 app/views.py:111 app/views.py:114
#: app/views.py:63 app/views.py:65 app/views.py:68 app/views.py:150
#: app/views.py:180 app/views.py:185 app/views.py:187 app/views.py:190
msgid "failure"
msgstr "failure"
#: app/views.py:48 app/views.py:50 app/views.py:53 app/views.py:97
#: app/views.py:102 app/views.py:106 app/views.py:109 app/views.py:111
#: app/views.py:114
#: app/views.py:63 app/views.py:65 app/views.py:68 app/views.py:146
#: app/views.py:150 app/views.py:171 app/views.py:176 app/views.py:180
#: app/views.py:185 app/views.py:187 app/views.py:190
msgid "messages"
msgstr "messages"
#: app/views.py:48 app/views.py:109
#: app/views.py:63 app/views.py:185
msgid "parameter_missing"
msgstr "Parámetro faltante: %s"
#: app/views.py:50 app/views.py:111
#: app/views.py:65 app/views.py:187
msgid "could_not_connect"
msgstr "No se pudo conectar a la Blockchain"
#: app/views.py:53 app/views.py:114
#: app/views.py:68 app/views.py:190
msgid "operation_failed"
msgstr "No se pudo realizar la operación"
#: app/views.py:97
#: app/views.py:145 app/views.py:171
msgid "permanent_ots"
msgstr "permanent_ots"
#: app/views.py:146 app/views.py:171
msgid "file_uploaded"
msgstr "El archivo %s fue ingresado en el bloque %s el %s"
#: app/views.py:102
#: app/views.py:150 app/views.py:180
msgid "file_not_found"
msgstr "No se encontró el archivo"
#: app/views.py:176
msgid "pending"
msgstr "pending"
#: app/views.py:102
#: app/views.py:176
msgid "transaction_pending"
msgstr "La transacción se encuentra pendiente de subida a la Blockchain"
#: app/views.py:106
msgid "file_not_found"
msgstr "No se encontró el archivo"
#~ msgid "proof"
#~ msgstr "proof"
pyethereum @ d962694b
Subproject commit d962694be03686a8e5c1d7459ae272b70a5c9f77
attrdict==2.0.0
certifi==2018.4.16
chardet==3.0.4
coreapi==2.3.3
coreschema==0.0.4
cytoolz==0.9.0.1
Django==2.1
django-cors-headers==2.4.0
django-rest-swagger==2.2.0
djangorestframework==3.8.2
djangorestframework-jwt==1.11.0
eth-abi==1.1.1
......@@ -15,7 +18,11 @@ eth-rlp==0.1.2
eth-utils==1.0.3
hexbytes==0.1.0
idna==2.7
itypes==1.1.0
Jinja2==2.10
lru-dict==1.1.6
MarkupSafe==1.0
openapi-codec==1.3.2
parsimonious==0.8.0
pkg-resources==0.0.0
pycryptodome==3.6.4
......@@ -25,8 +32,10 @@ pytz==2018.5
raven==6.9.0
requests==2.19.1
rlp==1.0.1
simplejson==3.16.0
six==1.11.0
toolz==0.9.0
uritemplate==3.0.0
urllib3==1.23
web3==4.5.0
websockets==5.0.1
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