Move common code to util module

This commit is contained in:
vrde 2016-02-22 23:46:32 +01:00
parent 6614f7a102
commit b3b54e7529
6 changed files with 71 additions and 65 deletions

View File

@ -1,12 +1,11 @@
import rethinkdb as r import rethinkdb as r
import time
import random import random
import json import json
import rapidjson import rapidjson
from datetime import datetime
import bigchaindb import bigchaindb
from bigchaindb import util
from bigchaindb import config_utils from bigchaindb import config_utils
from bigchaindb import exceptions from bigchaindb import exceptions
from bigchaindb.crypto import hash_data, PublicKey, PrivateKey, generate_key_pair from bigchaindb.crypto import hash_data, PublicKey, PrivateKey, generate_key_pair
@ -16,10 +15,6 @@ class GenesisBlockAlreadyExistsError(Exception):
pass pass
class KeypairNotFoundException(Exception):
pass
class Bigchain(object): class Bigchain(object):
"""Bigchain API """Bigchain API
@ -55,7 +50,7 @@ class Bigchain(object):
self.federation_nodes = keyring or bigchaindb.config['keyring'] self.federation_nodes = keyring or bigchaindb.config['keyring']
if not self.me or not self.me_private: if not self.me or not self.me_private:
raise KeypairNotFoundException() raise exceptions.KeypairNotFoundException()
self._conn = None self._conn = None
@ -100,7 +95,7 @@ class Bigchain(object):
data = None data = None
if payload is not None: if payload is not None:
if isinstance(payload, dict): if isinstance(payload, dict):
hash_payload = hash_data(self.serialize(payload)) hash_payload = hash_data(util.serialize(payload))
data = { data = {
'hash': hash_payload, 'hash': hash_payload,
'payload': payload 'payload': payload
@ -108,7 +103,7 @@ class Bigchain(object):
else: else:
raise TypeError('`payload` must be an dict instance') raise TypeError('`payload` must be an dict instance')
hash_payload = hash_data(self.serialize(payload)) hash_payload = hash_data(util.serialize(payload))
data = { data = {
'hash': hash_payload, 'hash': hash_payload,
'payload': payload 'payload': payload
@ -119,12 +114,12 @@ class Bigchain(object):
'new_owner': new_owner, 'new_owner': new_owner,
'input': tx_input, 'input': tx_input,
'operation': operation, 'operation': operation,
'timestamp': self.timestamp(), 'timestamp': util.timestamp(),
'data': data 'data': data
} }
# serialize and convert to bytes # serialize and convert to bytes
tx_serialized = self.serialize(tx) tx_serialized = util.serialize(tx)
tx_hash = hash_data(tx_serialized) tx_hash = hash_data(tx_serialized)
# create the transaction # create the transaction
@ -149,7 +144,7 @@ class Bigchain(object):
""" """
private_key = PrivateKey(private_key) private_key = PrivateKey(private_key)
signature = private_key.sign(self.serialize(transaction)) signature = private_key.sign(util.serialize(transaction))
signed_transaction = transaction.copy() signed_transaction = transaction.copy()
signed_transaction.update({'signature': signature}) signed_transaction.update({'signature': signature})
return signed_transaction return signed_transaction
@ -175,7 +170,7 @@ class Bigchain(object):
signature = data.pop('signature') signature = data.pop('signature')
public_key_base58 = signed_transaction['transaction']['current_owner'] public_key_base58 = signed_transaction['transaction']['current_owner']
public_key = PublicKey(public_key_base58) public_key = PublicKey(public_key_base58)
return public_key.verify(self.serialize(data), signature) return public_key.verify(util.serialize(data), signature)
def write_transaction(self, signed_transaction): def write_transaction(self, signed_transaction):
"""Write the transaction to bigchain. """Write the transaction to bigchain.
@ -360,7 +355,7 @@ class Bigchain(object):
transaction['transaction']['input'])) transaction['transaction']['input']))
# Check hash of the transaction # Check hash of the transaction
calculated_hash = hash_data(self.serialize(transaction['transaction'])) calculated_hash = hash_data(util.serialize(transaction['transaction']))
if calculated_hash != transaction['id']: if calculated_hash != transaction['id']:
raise exceptions.InvalidHash() raise exceptions.InvalidHash()
@ -405,14 +400,14 @@ class Bigchain(object):
""" """
# Create the new block # Create the new block
block = { block = {
'timestamp': self.timestamp(), 'timestamp': util.timestamp(),
'transactions': validated_transactions, 'transactions': validated_transactions,
'node_pubkey': self.me, 'node_pubkey': self.me,
'voters': self.federation_nodes + [self.me] 'voters': self.federation_nodes + [self.me]
} }
# Calculate the hash of the new block # Calculate the hash of the new block
block_data = self.serialize(block) block_data = util.serialize(block)
block_hash = hash_data(block_data) block_hash = hash_data(block_data)
block_signature = PrivateKey(self.me_private).sign(block_data) block_signature = PrivateKey(self.me_private).sign(block_data)
@ -439,7 +434,7 @@ class Bigchain(object):
""" """
# 1. Check if current hash is correct # 1. Check if current hash is correct
calculated_hash = hash_data(self.serialize(block['block'])) calculated_hash = hash_data(util.serialize(block['block']))
if calculated_hash != block['id']: if calculated_hash != block['id']:
raise exceptions.InvalidHash() raise exceptions.InvalidHash()
@ -531,10 +526,10 @@ class Bigchain(object):
'previous_block': previous_block_id, 'previous_block': previous_block_id,
'is_block_valid': decision, 'is_block_valid': decision,
'invalid_reason': invalid_reason, 'invalid_reason': invalid_reason,
'timestamp': self.timestamp() 'timestamp': util.timestamp()
} }
vote_data = self.serialize(vote) vote_data = util.serialize(vote)
signature = PrivateKey(self.me_private).sign(vote_data) signature = PrivateKey(self.me_private).sign(vote_data)
vote_signed = { vote_signed = {
@ -597,26 +592,6 @@ class Bigchain(object):
return unvoted return unvoted
@staticmethod
def serialize(data):
"""Static method used to serialize a dict into a JSON formatted string.
This method enforces rules like the separator and order of keys. This ensures that all dicts
are serialized in the same way.
This is specially important for hashing data. We need to make sure that everyone serializes their data
in the same way so that we do not have hash mismatches for the same structure due to serialization
differences.
Args:
data (dict): dict to serialize
Returns:
str: JSON formatted string
"""
return json.dumps(data, skipkeys=False, ensure_ascii=False,
separators=(',', ':'), sort_keys=True)
@staticmethod @staticmethod
def deserialize(data): def deserialize(data):
@ -631,17 +606,6 @@ class Bigchain(object):
""" """
return json.loads(data, encoding="utf-8") return json.loads(data, encoding="utf-8")
@staticmethod
def timestamp():
"""Static method to calculate a UTC timestamp with microsecond precision.
Returns:
str: UTC timestamp.
"""
dt = datetime.utcnow()
return "{0:.6f}".format(time.mktime(dt.timetuple()) + dt.microsecond / 1e6)
@staticmethod @staticmethod
def generate_keys(): def generate_keys():
"""Generates a key pair. """Generates a key pair.

View File

@ -25,3 +25,7 @@ class DatabaseAlreadyExists(Exception):
class DatabaseDoesNotExist(Exception): class DatabaseDoesNotExist(Exception):
"""Raised when trying to delete the database but the db is not there""" """Raised when trying to delete the database but the db is not there"""
class KeypairNotFoundException(Exception):
"""Raised if operation cannot proceed because the keypair was not given"""

View File

@ -1,4 +1,7 @@
import multiprocessing as mp import multiprocessing as mp
import json
import time
from datetime import datetime
class ProcessGroup(object): class ProcessGroup(object):
@ -22,3 +25,35 @@ class ProcessGroup(object):
proc.start() proc.start()
self.processes.append(proc) self.processes.append(proc)
def serialize(data):
"""Function used to serialize a dict into a JSON formatted string.
This function enforces rules like the separator and order of keys. This ensures that all dicts
are serialized in the same way.
This is specially important for hashing data. We need to make sure that everyone serializes their data
in the same way so that we do not have hash mismatches for the same structure due to serialization
differences.
Args:
data (dict): dict to serialize
Returns:
str: JSON formatted string
"""
return json.dumps(data, skipkeys=False, ensure_ascii=False,
separators=(',', ':'), sort_keys=True)
def timestamp():
"""Function to calculate a UTC timestamp with microsecond precision.
Returns:
str: UTC timestamp.
"""
dt = datetime.utcnow()
return "{0:.6f}".format(time.mktime(dt.timetuple()) + dt.microsecond / 1e6)

View File

@ -6,6 +6,7 @@ import pytest
import rethinkdb as r import rethinkdb as r
import bigchaindb import bigchaindb
from bigchaindb import util
from bigchaindb import exceptions from bigchaindb import exceptions
from bigchaindb import Bigchain from bigchaindb import Bigchain
from bigchaindb.crypto import hash_data, PrivateKey, PublicKey, generate_key_pair from bigchaindb.crypto import hash_data, PrivateKey, PublicKey, generate_key_pair
@ -69,7 +70,7 @@ class TestBigchainApi(object):
'operation': 'd', 'operation': 'd',
'timestamp': tx['transaction']['timestamp'], 'timestamp': tx['transaction']['timestamp'],
'data': { 'data': {
'hash': hash_data(b.serialize(payload)), 'hash': hash_data(util.serialize(payload)),
'payload': payload 'payload': payload
} }
} }
@ -86,7 +87,7 @@ class TestBigchainApi(object):
def test_serializer(self, b): def test_serializer(self, b):
tx = b.create_transaction('a', 'b', 'c', 'd') tx = b.create_transaction('a', 'b', 'c', 'd')
assert b.deserialize(b.serialize(tx)) == tx assert b.deserialize(util.serialize(tx)) == tx
@pytest.mark.usefixtures('inputs') @pytest.mark.usefixtures('inputs')
def test_write_transaction(self, b, user_public_key, user_private_key): def test_write_transaction(self, b, user_public_key, user_private_key):
@ -114,7 +115,7 @@ class TestBigchainApi(object):
b.write_block(block, durability='hard') b.write_block(block, durability='hard')
response = b.get_transaction(tx_signed["id"]) response = b.get_transaction(tx_signed["id"])
assert b.serialize(tx_signed) == b.serialize(response) assert util.serialize(tx_signed) == util.serialize(response)
@pytest.mark.usefixtures('inputs') @pytest.mark.usefixtures('inputs')
def test_assign_transaction_one_node(self, b, user_public_key, user_private_key): def test_assign_transaction_one_node(self, b, user_public_key, user_private_key):
@ -210,11 +211,11 @@ class TestBigchainApi(object):
def test_create_new_block(self, b): def test_create_new_block(self, b):
new_block = b.create_block([]) new_block = b.create_block([])
block_hash = hash_data(b.serialize(new_block['block'])) block_hash = hash_data(util.serialize(new_block['block']))
assert new_block['block']['voters'] == [b.me] assert new_block['block']['voters'] == [b.me]
assert new_block['block']['node_pubkey'] == b.me assert new_block['block']['node_pubkey'] == b.me
assert PublicKey(b.me).verify(b.serialize(new_block['block']), new_block['signature']) is True assert PublicKey(b.me).verify(util.serialize(new_block['block']), new_block['signature']) is True
assert new_block['id'] == block_hash assert new_block['id'] == block_hash
assert new_block['votes'] == [] assert new_block['votes'] == []
@ -378,7 +379,7 @@ class TestBlockValidation(object):
'voters': b.federation_nodes 'voters': b.federation_nodes
} }
block_data = b.serialize(block) block_data = util.serialize(block)
block_hash = hash_data(block_data) block_hash = hash_data(block_data)
block_signature = PrivateKey(b.me_private).sign(block_data) block_signature = PrivateKey(b.me_private).sign(block_data)
@ -491,7 +492,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is True assert vote['vote']['is_block_valid'] is True
assert vote['vote']['invalid_reason'] is None assert vote['vote']['invalid_reason'] is None
assert vote['node_pubkey'] == b.me assert vote['node_pubkey'] == b.me
assert PublicKey(b.me).verify(b.serialize(vote['vote']), vote['signature']) is True assert PublicKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
def test_invalid_block_voting(self, b, user_public_key): def test_invalid_block_voting(self, b, user_public_key):
# create queue and voter # create queue and voter
@ -533,7 +534,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is False assert vote['vote']['is_block_valid'] is False
assert vote['vote']['invalid_reason'] is None assert vote['vote']['invalid_reason'] is None
assert vote['node_pubkey'] == b.me assert vote['node_pubkey'] == b.me
assert PublicKey(b.me).verify(b.serialize(vote['vote']), vote['signature']) is True assert PublicKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
def test_vote_creation_valid(self, b): def test_vote_creation_valid(self, b):
# create valid block # create valid block
@ -547,7 +548,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is True assert vote['vote']['is_block_valid'] is True
assert vote['vote']['invalid_reason'] is None assert vote['vote']['invalid_reason'] is None
assert vote['node_pubkey'] == b.me assert vote['node_pubkey'] == b.me
assert PublicKey(b.me).verify(b.serialize(vote['vote']), vote['signature']) is True assert PublicKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
def test_vote_creation_invalid(self, b): def test_vote_creation_invalid(self, b):
# create valid block # create valid block
@ -561,7 +562,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is False assert vote['vote']['is_block_valid'] is False
assert vote['vote']['invalid_reason'] is None assert vote['vote']['invalid_reason'] is None
assert vote['node_pubkey'] == b.me assert vote['node_pubkey'] == b.me
assert PublicKey(b.me).verify(b.serialize(vote['vote']), vote['signature']) is True assert PublicKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
class TestBigchainBlock(object): class TestBigchainBlock(object):

View File

@ -4,6 +4,7 @@ import rethinkdb as r
import multiprocessing as mp import multiprocessing as mp
from bigchaindb import Bigchain from bigchaindb import Bigchain
from bigchaindb import util
from bigchaindb.voter import Voter, BlockStream from bigchaindb.voter import Voter, BlockStream
from bigchaindb.crypto import PublicKey from bigchaindb.crypto import PublicKey
@ -45,7 +46,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is True assert vote['vote']['is_block_valid'] is True
assert vote['vote']['invalid_reason'] is None assert vote['vote']['invalid_reason'] is None
assert vote['node_pubkey'] == b.me assert vote['node_pubkey'] == b.me
assert PublicKey(b.me).verify(b.serialize(vote['vote']), vote['signature']) is True assert PublicKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
def test_invalid_block_voting(self, b, user_public_key): def test_invalid_block_voting(self, b, user_public_key):
@ -86,7 +87,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is False assert vote['vote']['is_block_valid'] is False
assert vote['vote']['invalid_reason'] is None assert vote['vote']['invalid_reason'] is None
assert vote['node_pubkey'] == b.me assert vote['node_pubkey'] == b.me
assert PublicKey(b.me).verify(b.serialize(vote['vote']), vote['signature']) is True assert PublicKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
def test_vote_creation_valid(self, b): def test_vote_creation_valid(self, b):
# create valid block # create valid block
@ -100,7 +101,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is True assert vote['vote']['is_block_valid'] is True
assert vote['vote']['invalid_reason'] is None assert vote['vote']['invalid_reason'] is None
assert vote['node_pubkey'] == b.me assert vote['node_pubkey'] == b.me
assert PublicKey(b.me).verify(b.serialize(vote['vote']), vote['signature']) is True assert PublicKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
def test_vote_creation_invalid(self, b): def test_vote_creation_invalid(self, b):
# create valid block # create valid block
@ -114,7 +115,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is False assert vote['vote']['is_block_valid'] is False
assert vote['vote']['invalid_reason'] is None assert vote['vote']['invalid_reason'] is None
assert vote['node_pubkey'] == b.me assert vote['node_pubkey'] == b.me
assert PublicKey(b.me).verify(b.serialize(vote['vote']), vote['signature']) is True assert PublicKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
def test_voter_considers_unvoted_blocks_when_single_node(self, b): def test_voter_considers_unvoted_blocks_when_single_node(self, b):
# simulate a voter going donw in a single node environment # simulate a voter going donw in a single node environment

View File

@ -3,6 +3,7 @@ import copy
import pytest import pytest
import bigchaindb import bigchaindb
from bigchaindb import exceptions
ORIGINAL_CONFIG = copy.deepcopy(bigchaindb.config) ORIGINAL_CONFIG = copy.deepcopy(bigchaindb.config)
@ -34,5 +35,5 @@ def test_bigchain_instance_raises_when_not_configured(monkeypatch):
# from existing configurations # from existing configurations
monkeypatch.setattr(config_utils, 'autoconfigure', lambda: 0) monkeypatch.setattr(config_utils, 'autoconfigure', lambda: 0)
with pytest.raises(bigchaindb.core.KeypairNotFoundException): with pytest.raises(exceptions.KeypairNotFoundException):
bigchaindb.Bigchain() bigchaindb.Bigchain()