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 time
import random
import json
import rapidjson
from datetime import datetime
import bigchaindb
from bigchaindb import util
from bigchaindb import config_utils
from bigchaindb import exceptions
from bigchaindb.crypto import hash_data, PublicKey, PrivateKey, generate_key_pair
@ -16,10 +15,6 @@ class GenesisBlockAlreadyExistsError(Exception):
pass
class KeypairNotFoundException(Exception):
pass
class Bigchain(object):
"""Bigchain API
@ -55,7 +50,7 @@ class Bigchain(object):
self.federation_nodes = keyring or bigchaindb.config['keyring']
if not self.me or not self.me_private:
raise KeypairNotFoundException()
raise exceptions.KeypairNotFoundException()
self._conn = None
@ -100,7 +95,7 @@ class Bigchain(object):
data = None
if payload is not None:
if isinstance(payload, dict):
hash_payload = hash_data(self.serialize(payload))
hash_payload = hash_data(util.serialize(payload))
data = {
'hash': hash_payload,
'payload': payload
@ -108,7 +103,7 @@ class Bigchain(object):
else:
raise TypeError('`payload` must be an dict instance')
hash_payload = hash_data(self.serialize(payload))
hash_payload = hash_data(util.serialize(payload))
data = {
'hash': hash_payload,
'payload': payload
@ -119,12 +114,12 @@ class Bigchain(object):
'new_owner': new_owner,
'input': tx_input,
'operation': operation,
'timestamp': self.timestamp(),
'timestamp': util.timestamp(),
'data': data
}
# serialize and convert to bytes
tx_serialized = self.serialize(tx)
tx_serialized = util.serialize(tx)
tx_hash = hash_data(tx_serialized)
# create the transaction
@ -149,7 +144,7 @@ class Bigchain(object):
"""
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.update({'signature': signature})
return signed_transaction
@ -175,7 +170,7 @@ class Bigchain(object):
signature = data.pop('signature')
public_key_base58 = signed_transaction['transaction']['current_owner']
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):
"""Write the transaction to bigchain.
@ -360,7 +355,7 @@ class Bigchain(object):
transaction['transaction']['input']))
# 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']:
raise exceptions.InvalidHash()
@ -405,14 +400,14 @@ class Bigchain(object):
"""
# Create the new block
block = {
'timestamp': self.timestamp(),
'timestamp': util.timestamp(),
'transactions': validated_transactions,
'node_pubkey': self.me,
'voters': self.federation_nodes + [self.me]
}
# Calculate the hash of the new block
block_data = self.serialize(block)
block_data = util.serialize(block)
block_hash = hash_data(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
calculated_hash = hash_data(self.serialize(block['block']))
calculated_hash = hash_data(util.serialize(block['block']))
if calculated_hash != block['id']:
raise exceptions.InvalidHash()
@ -531,10 +526,10 @@ class Bigchain(object):
'previous_block': previous_block_id,
'is_block_valid': decision,
'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)
vote_signed = {
@ -597,26 +592,6 @@ class Bigchain(object):
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
def deserialize(data):
@ -631,17 +606,6 @@ class Bigchain(object):
"""
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
def generate_keys():
"""Generates a key pair.

View File

@ -25,3 +25,7 @@ class DatabaseAlreadyExists(Exception):
class DatabaseDoesNotExist(Exception):
"""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 json
import time
from datetime import datetime
class ProcessGroup(object):
@ -22,3 +25,35 @@ class ProcessGroup(object):
proc.start()
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 bigchaindb
from bigchaindb import util
from bigchaindb import exceptions
from bigchaindb import Bigchain
from bigchaindb.crypto import hash_data, PrivateKey, PublicKey, generate_key_pair
@ -69,7 +70,7 @@ class TestBigchainApi(object):
'operation': 'd',
'timestamp': tx['transaction']['timestamp'],
'data': {
'hash': hash_data(b.serialize(payload)),
'hash': hash_data(util.serialize(payload)),
'payload': payload
}
}
@ -86,7 +87,7 @@ class TestBigchainApi(object):
def test_serializer(self, b):
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')
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')
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')
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):
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']['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['votes'] == []
@ -378,7 +379,7 @@ class TestBlockValidation(object):
'voters': b.federation_nodes
}
block_data = b.serialize(block)
block_data = util.serialize(block)
block_hash = hash_data(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']['invalid_reason'] is None
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):
# create queue and voter
@ -533,7 +534,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is False
assert vote['vote']['invalid_reason'] is None
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):
# create valid block
@ -547,7 +548,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is True
assert vote['vote']['invalid_reason'] is None
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):
# create valid block
@ -561,7 +562,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is False
assert vote['vote']['invalid_reason'] is None
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):

View File

@ -4,6 +4,7 @@ import rethinkdb as r
import multiprocessing as mp
from bigchaindb import Bigchain
from bigchaindb import util
from bigchaindb.voter import Voter, BlockStream
from bigchaindb.crypto import PublicKey
@ -45,7 +46,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is True
assert vote['vote']['invalid_reason'] is None
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):
@ -86,7 +87,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is False
assert vote['vote']['invalid_reason'] is None
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):
# create valid block
@ -100,7 +101,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is True
assert vote['vote']['invalid_reason'] is None
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):
# create valid block
@ -114,7 +115,7 @@ class TestBigchainVoter(object):
assert vote['vote']['is_block_valid'] is False
assert vote['vote']['invalid_reason'] is None
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):
# simulate a voter going donw in a single node environment

View File

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