Skip to content
Snippets Groups Projects
Commit 560c9157 authored by Agustin Dorda's avatar Agustin Dorda
Browse files

Servicio de verify con translations

parent 7bd7d15b
No related branches found
No related tags found
No related merge requests found
FROM python:3.5
RUN apt-get update && apt-get install -y --no-install-recommends apt-utils
RUN apt-get update && apt-get -y upgrade && apt-get install -y apt-transport-https software-properties-common libsasl2-dev python-dev libldap2-dev libssl-dev memcached
FROM python:3.5.2
RUN apt-get update && apt-get -y upgrade && apt-get install -y --no-install-recommends apt-utils && apt-get install -y apt-transport-https software-properties-common libsasl2-dev python-dev libldap2-dev libssl-dev memcached
RUN apt-get install -y gettext
RUN git clone https://github.com/ethereum/pyethereum/ /opt/pyethereum
WORKDIR /opt/pyethereum
RUN python setup.py install
RUN mkdir -p /opt/project
WORKDIR /opt/project
ADD requirements.txt /opt/project
RUN pip install -U pip
RUN pip install -r requirements.txt
WORKDIR /opt/pyethereum
RUN python setup.py install
RUN mkdir -p /opt/project
WORKDIR /opt/project
ADD . /opt/project
\ No newline at end of file
......@@ -64,11 +64,11 @@ class TimestampManager(models.Manager):
def stamp(proof_hash, file_hash):
contract = TimestampManager.get_current_contract()
web3 = TimestampManager.get_provider()
return contract.functions.stamp(proof_hash, file_hash).transact({'from': Web3.toChecksumAddress(ACCOUNT_ADDRESS), 'gas': GAS, 'gasPrice': web3.eth.gasPrice})
web3 = TimestampManager.get_provider()
return contract.functions.stamp(proof_hash, file_hash).transact(
{'from': Web3.toChecksumAddress(ACCOUNT_ADDRESS), 'gas': GAS, 'gasPrice': web3.eth.gasPrice})
@staticmethod
def verify(contract_version, proof_hash, file_hash):
......@@ -105,7 +105,7 @@ class EthereumGateway:
last_block_number = TimestampManager.get_last_block_number()
tx_block_number = transaction.blockNumber
required_block_difference = Utils.required_block_difference(TimestampManager.get_signers_count())
return (last_block_number - tx_block_number) < required_block_difference
return (last_block_number - tx_block_number) > required_block_difference
def transaction(self, tx_hash):
return TimestampManager.get_transaction(tx_hash)
......@@ -125,7 +125,7 @@ class EthereumGateway:
w3 = TimestampManager.get_provider()
tx_2 = w3.eth.getTransaction(tx['hash'])
if tx_2['blockNumber'] is not None:
raise TxAlreadySealedInBlock("Tx already seald in block {}".format(str(tx_2['blockNumber'])))
raise TxAlreadySealedInBlock("Tx already sealed in block {}".format(str(tx_2['blockNumber'])))
return w3.eth.resend(tx, gas_price, gas)
......@@ -135,4 +135,7 @@ class TimeStamp:
return CONTRACTS[CURRENT_CONTRACT_VERSION]['abi']
def verify(self, proof_hash, file_hash):
return TimestampManager.verify(CURRENT_CONTRACT_VERSION, proof_hash, file_hash)
\ No newline at end of file
return TimestampManager.verify(CURRENT_CONTRACT_VERSION, proof_hash, file_hash)
def get_block(self, proof_hash):
return TimestampManager.get_block_number(CURRENT_CONTRACT_VERSION, proof_hash)
......@@ -9,6 +9,8 @@ import logging
import json
from sys import stdout
from django.utils.translation import gettext as _, gettext
if stdout.isatty():
log_str = 'console-logger'
else:
......@@ -16,14 +18,6 @@ else:
module_logger = logging.getLogger(log_str)
class DefinitiveReceiptGenerator:
encoder = Base64EncodingService()
def generate_definitive_receipt(self, original_file_hash, ots_hash, tx_hash, block):
base64.b64encode(
Utils.get_permanent_ots(original_file_hash, ots_hash, tx_hash,
block.number).encode('utf-8')).decode('utf-8')
class ReceiptInterpreter:
......@@ -31,13 +25,26 @@ class ReceiptInterpreter:
return ots.split('-')
class DefinitiveReceiptGenerator:
encoder = Base64EncodingService()
interpreter = ReceiptInterpreter()
gateway = EthereumGateway()
def generate_definitive_receipt(self, original_file_hash, temp_rd):
ots_version, ots_hash, tx_hash = self.interpreter.interpret(temp_rd)
tx = self.gateway.transaction(tx_hash)
return self.encoder.encode(Utils.get_permanent_ots(original_file_hash, ots_hash, tx_hash,
tx['blockNumber']).encode('utf-8')).decode('utf-8')
class TransactionInputVerification:
gateway = EthereumGateway()
interpreter = ReceiptInterpreter()
ts = TimeStamp()
tx_hash = ''
def verify(self, ots):
def verify(self, original_file_hash, ots):
ots_version, file_hash, ots_hash, tx_hash, block_number = self.interpreter.interpret(ots)
method_name, args = Utils.decode_contract_call(self.ts.abi(), TimestampManager.get_transaction(tx_hash).input)
return args[0].decode('utf-8') == ots_hash and args[1].decode('utf-8') == file_hash
......@@ -46,61 +53,82 @@ class TransactionInputVerification:
class SmartContractVerification:
timestamp = TimeStamp()
interpreter = ReceiptInterpreter()
gateway = EthereumGateway()
def verify(self, ots):
ots_version, file_hash, ots_hash, tx_hash, block_number = self.interpreter.interpret(ots)
self.timestamp.verify(ots_hash, file_hash)
def verify(self, file_hash, ots_hash):
verified = self.timestamp.verify(ots_hash, file_hash)
block = None
if verified:
block = self.timestamp.get_block(ots_hash)
return verified, block
class EventVerification:
gateway = EthereumGateway()
class VerifyService:
receipt_generator = DefinitiveReceiptGenerator()
definitive_verificator = TransactionInputVerification()
definitive_verificator = SmartContractVerification()
temp_verificator = SmartContractVerification()
interpreter = ReceiptInterpreter()
gateway = EthereumGateway()
permanent_ots_prefix = ''
def verify(self, original_file_hash, ots):
if ots[:2] == self.permanent_ots_prefix:
if self.definitive_verificator.verify(ots):
block = TimestampManager.get_block(int(block_number))
return {
'status': 'success',
'messages': ('file_uploaded') % (str(original_file_hash), str(block.number),
str(Utils.datetime_from_timestamp(block.timestamp)))}
def verify(self, original_file_hash, rd):
if rd[:2] == self.permanent_ots_prefix:
ots_version, file_hash, ots_hash, tx_hash, block_number = self.interpreter.interpret(rd)
verified, block = self.definitive_verificator.verify(original_file_hash, ots_hash)
if verified:
return {'status': 'success', 'block': block}
else:
return {'status': 'failure', 'messages': 'file_not_found'}
return {'status': 'failure'}
else:
ots_version, ots_hash, tx_hash = ots.split('-')
transaction = TimestampManager.get_transaction(tx_hash)
contract_version = ots_hash[-2:]
if TimestampManager.verify(contract_version, ots_hash, original_file_hash):
if (
TimestampManager.get_last_block_number() - transaction.blockNumber) < Utils.required_block_difference(
TimestampManager.get_signers_count()):
return {'status': 'pending', 'messages': 'transaction_pending'}
ots_version, ots_hash, tx_hash = self.interpreter.interpret(rd)
verified, block = self.temp_verificator.verify(original_file_hash, ots_hash)
tx = self.gateway.transaction(tx_hash)
if verified:
if not self.gateway.transaction_is_canonical(tx):
return {'status': 'pending'}
else:
block = TimestampManager.get_block(
TimestampManager.get_block_number(contract_version, ots_hash))
return {'status': 'success',
'permanent_rd': base64.b64encode(
Utils.get_permanent_ots(original_file_hash, ots_hash, tx_hash,
block.number).encode('utf-8')).decode('utf-8'),
'messages': 'file_uploaded' % (
original_file_hash, str(block.number), str(
Utils.datetime_from_timestamp(block.timestamp)))}
'block': block,
'permanent_rd': self.receipt_generator.generate_definitive_receipt(original_file_hash, rd)}
else:
try:
if transaction and not transaction.blockNumber:
return {'status': 'pending', 'messages': 'transaction_pending'}
if tx and not tx.blockNumber:
return {'status': 'pending'}
except ValueError:
pass
return {'status': 'failure', 'messages': 'file_not_found'}
return {'status': 'failure'}
class StatusNotValidException(Exception):
pass
class VerifyServiceTranslation:
verify_service = VerifyService()
def verify(self, original_file_hash, rd):
result = self.verify_service.verify(original_file_hash, rd)
dict_res = {gettext('status'): result['status']}
if result['status'] == 'success':
dict_res[gettext('permanent_rd')] = result['permanent_rd']
dict_res[gettext('attestation_time')] = str(Utils.datetime_from_timestamp(result['block'].timestamp))
dict_res[gettext('messages')] = gettext('file_uploaded') % (
original_file_hash,
str(result['block'].number),
str(Utils.datetime_from_timestamp(result['block'].timestamp))
)
elif result['status'] == 'failure':
dict_res[gettext('messages')] = result[gettext('file_not_found')]
elif result['status'] == 'pending':
dict_res[gettext('messages')] = result[gettext('transaction_pending')]
else:
raise StatusNotValidException('Status invalid in verify service result : {}'.format(result['status']))
class MemcachedStorage:
......
......@@ -7,37 +7,45 @@ This program is distributed in the hope that it will be useful, but WITHOUT ANY
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/
"""
import base64
import json
import time
from django.utils.translation import gettext as _
from django.test import TestCase
from django.test import Client
from eth_account.datastructures import AttributeDict
from pymemcache.client import base
from rest_framework import status
from TsaApi.settings import TEST_MEMCACHED_HOST, TEST_MEMCACHED_PORT, TEST_PENDING_TXS_MEMCACHED_KEY
from TsaApi.settings import TEST_MEMCACHED_HOST, TEST_MEMCACHED_PORT, TEST_PENDING_TXS_MEMCACHED_KEY, \
PERMANENT_OTS_PREFIX
from app.services import PendingTransactionsService, CheckCriteria, MemcachedStorage, TransactionUnstucker
from app.services import PendingTransactionsService, CheckCriteria, MemcachedStorage, TransactionUnstucker, \
VerifyService
class TimestampTest(TestCase):
def test_verify(self):
c = Client()
response = c.post('/api/tsa/verify/', {
"file_hash": "1957db7fe23e4be1740ddeb941ddda7ae0a6b782e536a9e00b5aa82db1e84547",
"proof": "NjA2MWQ1ODNkMTM0YTlmZGZlYWYxMWE2MjY1ZDY1OTkwMGM0ZGZjNjM2MDk3MDY4N2VkNDk3NTNhZmE5MGM3YzAxLTB4NWRiYjg4NjNlNDlhN2NmYTY0N2FmYzVkOTFkMWRiMDUxY2YzODZiYWY4NzJkM2ZiYTIzN2JkY2IwOWUwNWMzYQ=="
})
self.assertEqual(response.status_code, status.HTTP_200_OK, 'Verify Error')
def test_stamp(self):
def test_full_cycle(self):
c = Client()
response = c.post('/api/tsa/stamp/', {
"file_hash": "1957db7fe23e4be1740ddeb941ddda7ae0a6b782e536a9e00b5aa82db1e84547"
})
self.assertEqual(response.status_code, status.HTTP_200_OK, 'Stamp Error')
dict_response = json.loads(response.content.decode())
time.sleep(30)
verify_response = c.post('/api/tsa/verify/', {
"file_hash": "1957db7fe23e4be1740ddeb941ddda7ae0a6b782e536a9e00b5aa82db1e84547",
"rd": dict_response['temporary_rd']
})
self.assertEqual(verify_response.status_code, status.HTTP_200_OK, 'Verify Error')
dict_verify = json.loads(verify_response.content.decode())
self.assertEqual(dict_verify[_('status')], _('success'))
class EthereumMocks():
class EthereumMocks:
class HalfResender:
balance = 2.1 * 21000 * 1000 # me da para 2 txs mockeadas
......@@ -360,3 +368,86 @@ class TransactionUnstuckerTest(TestCase, EthereumMocks):
res, failed = self.unstucker.unstuck_pending_transactions(1000)
self.assertEqual(failed, [])
self.assertEqual(res, []) # como no tenía cache entonces no se hizo nada
class VerificationMocks:
class NotSealedTransactionMock:
def transaction(self, tx_hash):
return AttributeDict(
{'hash': '0xf495705b5d7d68afdcefe72d9bbacec59b1a968c964ab0b6b13cec6491e5ddec', 'blockNumber': None})
class SealedTransactionMock:
def transaction(self, tx_hash):
return AttributeDict(
{'hash': '0xf495705b5d7d68afdcefe72d9bbacec59b1a968c964ab0b6b13cec6491e5ddec', 'blockNumber': 1000})
class TXisCanonicalMock:
def transaction_is_canonical(self, tx):
return True
class TXisNotCanonicalMock:
def transaction_is_canonical(self, tx):
return False
class FalseVerificationMock:
def verify(self, file_hash, ots_hash):
return False, None
class TrueVerificationMock:
def verify(self, file_hash, ots_hash):
return True, AttributeDict({'number': 1000})
class TxCanonicalSealedTx(SealedTransactionMock, TXisCanonicalMock):
pass
class TxNoCanonicalSealedTx(SealedTransactionMock, TXisNotCanonicalMock):
pass
class VerifyServiceTest(TestCase, VerificationMocks):
def setUp(self):
self.service = VerifyService()
self.service.permanent_ots_prefix = PERMANENT_OTS_PREFIX
self.ots = "d6b88be854cb88cf1e161d8bbd912987cff236eb43a3a15fff86370054dd4d3f01"
self.file_hash = 'some_hash'
self.contract_version = '01'
self.temp_rd = "0x-d6b88be854cb88cf1e161d8bbd912987cff236eb43a3a15fff86370054dd4d3f01-0xf495705b5d7d68afdcefe72d9bbacec59b1a968c964ab0b6b13cec6491e5ddec"
self.def_rd = "1x-7b58a42ee27c3699fc169ce43cb4ba598003cba5699f4cd518b91d4177f61f10-0577d1390a5733534c7b6cc45a7ba15d0b5bf37faec9c54f749826af75aa86bb01-0x1a3a5335b44bdb7f62201102e91658586fd1d7845c621503eeff7781c3020b09-2234445"
pass
def test_verify_temporary_rd_not_sealed(self):
self.service.gateway = self.NotSealedTransactionMock()
self.service.temp_verificator = self.FalseVerificationMock()
res = self.service.verify(self.file_hash, self.temp_rd)
self.assertEqual(res['status'], 'pending')
def test_verify_temporary_rd_sealed_not_canonical(self):
self.service.gateway = self.TxNoCanonicalSealedTx()
self.service.temp_verificator = self.TrueVerificationMock()
res = self.service.verify(self.file_hash, self.temp_rd)
self.assertEqual(res['status'], 'pending')
def test_verify_temporary_rd_sealed_canonical(self):
self.service.temp_verificator = self.TrueVerificationMock()
self.service.gateway = self.TxCanonicalSealedTx()
self.service.receipt_generator.gateway = self.SealedTransactionMock()
res = self.service.verify(self.file_hash, self.temp_rd)
self.assertEqual(res['status'], 'success')
rd = res['permanent_rd']
ots_version, file_hash, ots_hash, tx_hash, block_number = base64.b64decode(rd).decode().split('-')
self.assertEqual(ots_version, PERMANENT_OTS_PREFIX)
self.assertEqual(file_hash, self.file_hash)
self.assertEqual(ots_hash, self.ots)
self.assertEqual(res['block'].number, int(block_number))
def test_definitive_rd_correct(self):
self.service.definitive_verificator = self.TrueVerificationMock()
res = self.service.verify(self.file_hash, self.def_rd)
self.assertEqual(res['status'], 'success')
def test_definitive_rd_incorrect(self):
res = self.service.verify(self.file_hash, self.def_rd)
self.assertEqual(res['status'], 'failure')
......@@ -13,8 +13,6 @@ import hashlib
import time
import math
from TsaApi.settings import ACCOUNT_ADDRESS, CURRENT_CONTRACT_VERSION, PERMANENT_OTS_PREFIX, TEMPORARY_OTS_PREFIX
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:
......
......@@ -19,6 +19,7 @@ from rest_framework.schemas import ManualSchema
import coreschema, coreapi
from app.managers import TimestampManager
from app.services import VerifyService, VerifyServiceTranslation
from app.utils import Utils
from TsaApi.local_settings import TEMPORARY_OTS_PREFIX, PERMANENT_OTS_PREFIX, CONTRACTS
......@@ -117,6 +118,8 @@ class Verify(APIView):
)
])
verify_service = VerifyServiceTranslation()
def post(self, request):
try:
......@@ -127,68 +130,13 @@ class Verify(APIView):
raise ValidationError('rd')
original_file_hash = request.data.get('file_hash')
base64_ots = request.data.get('rd')
ots = base64.b64decode(base64_ots).decode('utf-8')
if ots[:2] == PERMANENT_OTS_PREFIX:
ots_version, file_hash, ots_hash, tx_hash, block_number = ots.split('-')
method_name, args = Utils.decode_contract_call(CONTRACTS['01']['abi'],
TimestampManager.get_transaction(tx_hash).input)
if args[0].decode('utf-8') == ots_hash and args[1].decode('utf-8') == file_hash:
block = TimestampManager.get_block(int(block_number))
return Response({_('status'): _('success'),
_('permanent_rd'): base64.b64encode(Utils.get_permanent_ots(original_file_hash, ots_hash, tx_hash, block.number).encode('utf-8')).decode('utf-8'),
_('attestation_time'): str(Utils.datetime_from_timestamp(block.timestamp)),
_('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)
else:
ots_version, ots_hash, tx_hash = ots.split('-')
transaction = TimestampManager.get_transaction(tx_hash)
contract_version = ots_hash[-2:]
if TimestampManager.verify(contract_version, ots_hash, original_file_hash):
if (
TimestampManager.get_last_block_number() - transaction.blockNumber) < Utils.required_block_difference(
TimestampManager.get_signers_count()):
return Response({_('status'): _('pending'), _('messages'): _('transaction_pending')},
status=status.HTTP_200_OK)
else:
block = TimestampManager.get_block(
TimestampManager.get_block_number(contract_version, ots_hash))
base64_rd = request.data.get('rd')
return Response({_('status'): _('success'),
_('permanent_rd'): base64.b64encode(Utils.get_permanent_ots(original_file_hash, ots_hash, tx_hash, block.number).encode('utf-8')).decode('utf-8'),
_('attestation_time'): str(Utils.datetime_from_timestamp(block.timestamp)),
_('messages'): _('file_uploaded') % (original_file_hash, str(block.number), str(
Utils.datetime_from_timestamp(block.timestamp)))}, status=status.HTTP_200_OK)
else:
try:
if transaction and not transaction.blockNumber:
return Response({_('status'): _('pending'), _('messages'): _('transaction_pending')},
status=status.HTTP_200_OK)
except ValueError:
pass
rd = base64.b64decode(base64_rd).decode('utf-8')
return Response({_('status'): _('failure'), _('messages'): _('file_not_found')},
status=status.HTTP_404_NOT_FOUND)
result = self.verify_service.verify(original_file_hash, rd)
return Response(result)
except ValidationError as e:
return Response({_('status'): _('failure'), _('messages'): _('parameter_missing') % e.message},
status=status.HTTP_400_BAD_REQUEST)
......
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