mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Merge branch 'master' into resolve-issue-458
This commit is contained in:
commit
6ad8448246
@ -492,6 +492,16 @@ class Bigchain(object):
|
|||||||
response = r.table('bigchain').get_all(transaction_id, index='transaction_id').run(self.conn)
|
response = r.table('bigchain').get_all(transaction_id, index='transaction_id').run(self.conn)
|
||||||
return True if len(response.items) > 0 else False
|
return True if len(response.items) > 0 else False
|
||||||
|
|
||||||
|
def prepare_genesis_block(self):
|
||||||
|
"""Prepare a genesis block."""
|
||||||
|
|
||||||
|
payload = {'message': 'Hello World from the BigchainDB'}
|
||||||
|
transaction = self.create_transaction([self.me], [self.me], None, 'GENESIS', payload=payload)
|
||||||
|
transaction_signed = self.sign_transaction(transaction, self.me_private)
|
||||||
|
|
||||||
|
# create the block
|
||||||
|
return self.create_block([transaction_signed])
|
||||||
|
|
||||||
# TODO: Unless we prescribe the signature of create_transaction, this will
|
# TODO: Unless we prescribe the signature of create_transaction, this will
|
||||||
# also need to be moved into the plugin API.
|
# also need to be moved into the plugin API.
|
||||||
def create_genesis_block(self):
|
def create_genesis_block(self):
|
||||||
@ -511,14 +521,7 @@ class Bigchain(object):
|
|||||||
if blocks_count:
|
if blocks_count:
|
||||||
raise exceptions.GenesisBlockAlreadyExistsError('Cannot create the Genesis block')
|
raise exceptions.GenesisBlockAlreadyExistsError('Cannot create the Genesis block')
|
||||||
|
|
||||||
payload = {'message': 'Hello World from the BigchainDB'}
|
block = self.prepare_genesis_block()
|
||||||
transaction = self.create_transaction([self.me], [self.me], None, 'GENESIS', payload=payload)
|
|
||||||
transaction_signed = self.sign_transaction(transaction, self.me_private)
|
|
||||||
|
|
||||||
# create the block
|
|
||||||
block = self.create_block([transaction_signed])
|
|
||||||
# add block number before writing
|
|
||||||
block['block_number'] = 0
|
|
||||||
self.write_block(block, durability='hard')
|
self.write_block(block, durability='hard')
|
||||||
|
|
||||||
return block
|
return block
|
||||||
@ -534,6 +537,9 @@ class Bigchain(object):
|
|||||||
invalid_reason (Optional[str]): Reason the block is invalid
|
invalid_reason (Optional[str]): Reason the block is invalid
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if block['id'] == previous_block_id:
|
||||||
|
raise exceptions.CyclicBlockchainError()
|
||||||
|
|
||||||
vote = {
|
vote = {
|
||||||
'voting_for_block': block['id'],
|
'voting_for_block': block['id'],
|
||||||
'previous_block': previous_block_id,
|
'previous_block': previous_block_id,
|
||||||
@ -553,18 +559,13 @@ class Bigchain(object):
|
|||||||
|
|
||||||
return vote_signed
|
return vote_signed
|
||||||
|
|
||||||
def write_vote(self, block, vote, block_number):
|
def write_vote(self, block, vote):
|
||||||
"""Write the vote to the database."""
|
"""Write the vote to the database."""
|
||||||
|
|
||||||
# First, make sure this block doesn't contain a vote from this node
|
# First, make sure this block doesn't contain a vote from this node
|
||||||
if self.has_previous_vote(block):
|
if self.has_previous_vote(block):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# We need to *not* override the existing block_number, if any
|
|
||||||
# FIXME: MIGHT HAVE RACE CONDITIONS WITH THE OTHER NODES IN THE FEDERATION
|
|
||||||
if 'block_number' not in vote:
|
|
||||||
vote['block_number'] = block_number # maybe this should be in the signed part...or better yet, removed..
|
|
||||||
|
|
||||||
r.table('votes') \
|
r.table('votes') \
|
||||||
.insert(vote) \
|
.insert(vote) \
|
||||||
.run(self.conn)
|
.run(self.conn)
|
||||||
@ -572,22 +573,55 @@ class Bigchain(object):
|
|||||||
def get_last_voted_block(self):
|
def get_last_voted_block(self):
|
||||||
"""Returns the last block that this node voted on."""
|
"""Returns the last block that this node voted on."""
|
||||||
|
|
||||||
last_voted = r.table('votes') \
|
try:
|
||||||
|
# get the latest value for the vote timestamp (over all votes)
|
||||||
|
max_timestamp = r.table('votes') \
|
||||||
.filter(r.row['node_pubkey'] == self.me) \
|
.filter(r.row['node_pubkey'] == self.me) \
|
||||||
.order_by(r.desc('block_number')) \
|
.max(r.row['vote']['timestamp']) \
|
||||||
.limit(1) \
|
.run(self.conn)['vote']['timestamp']
|
||||||
.run(self.conn)
|
|
||||||
|
|
||||||
|
last_voted = list(r.table('votes') \
|
||||||
|
.filter(r.row['vote']['timestamp'] == max_timestamp) \
|
||||||
|
.filter(r.row['node_pubkey'] == self.me) \
|
||||||
|
.run(self.conn))
|
||||||
|
|
||||||
|
except r.ReqlNonExistenceError:
|
||||||
# return last vote if last vote exists else return Genesis block
|
# return last vote if last vote exists else return Genesis block
|
||||||
if not last_voted:
|
|
||||||
return list(r.table('bigchain')
|
return list(r.table('bigchain')
|
||||||
.filter(r.row['block_number'] == 0)
|
.filter(util.is_genesis_block)
|
||||||
.run(self.conn))[0]
|
.run(self.conn))[0]
|
||||||
|
|
||||||
res = r.table('bigchain').get(last_voted[0]['vote']['voting_for_block']).run(self.conn)
|
# Now the fun starts. Since the resolution of timestamp is a second,
|
||||||
|
# we might have more than one vote per timestamp. If this is the case
|
||||||
|
# then we need to rebuild the chain for the blocks that have been retrieved
|
||||||
|
# to get the last one.
|
||||||
|
|
||||||
if 'block_number' in last_voted[0]:
|
# Given a block_id, mapping returns the id of the block pointing at it.
|
||||||
res['block_number'] = last_voted[0]['block_number']
|
mapping = {v['vote']['previous_block']: v['vote']['voting_for_block']
|
||||||
|
for v in last_voted}
|
||||||
|
|
||||||
|
# Since we follow the chain backwards, we can start from a random
|
||||||
|
# point of the chain and "move up" from it.
|
||||||
|
last_block_id = list(mapping.values())[0]
|
||||||
|
|
||||||
|
# We must be sure to break the infinite loop. This happens when:
|
||||||
|
# - the block we are currenty iterating is the one we are looking for.
|
||||||
|
# This will trigger a KeyError, breaking the loop
|
||||||
|
# - we are visiting again a node we already explored, hence there is
|
||||||
|
# a loop. This might happen if a vote points both `previous_block`
|
||||||
|
# and `voting_for_block` to the same `block_id`
|
||||||
|
explored = set()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
if last_block_id in explored:
|
||||||
|
raise exceptions.CyclicBlockchainError()
|
||||||
|
explored.add(last_block_id)
|
||||||
|
last_block_id = mapping[last_block_id]
|
||||||
|
except KeyError:
|
||||||
|
break
|
||||||
|
|
||||||
|
res = r.table('bigchain').get(last_block_id).run(self.conn)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@ -597,13 +631,14 @@ class Bigchain(object):
|
|||||||
unvoted = r.table('bigchain') \
|
unvoted = r.table('bigchain') \
|
||||||
.filter(lambda block: r.table('votes').get_all([block['id'], self.me], index='block_and_voter')
|
.filter(lambda block: r.table('votes').get_all([block['id'], self.me], index='block_and_voter')
|
||||||
.is_empty()) \
|
.is_empty()) \
|
||||||
.order_by(r.desc('block_number')) \
|
.order_by(r.asc(r.row['block']['timestamp'])) \
|
||||||
.run(self.conn)
|
.run(self.conn)
|
||||||
|
|
||||||
if unvoted and unvoted[0].get('block_number') == 0:
|
# FIXME: I (@vrde) don't like this solution. Filtering should be done at a
|
||||||
unvoted.pop(0)
|
# database level. Solving issue #444 can help untangling the situation
|
||||||
|
unvoted = filter(lambda block: not util.is_genesis_block(block), unvoted)
|
||||||
|
|
||||||
return unvoted
|
return list(unvoted)
|
||||||
|
|
||||||
def block_election_status(self, block):
|
def block_election_status(self, block):
|
||||||
"""Tally the votes on a block, and return the status: valid, invalid, or undecided."""
|
"""Tally the votes on a block, and return the status: valid, invalid, or undecided."""
|
||||||
|
@ -29,10 +29,6 @@ def init_bigchain_table(conn, dbname):
|
|||||||
r.db(dbname).table('bigchain')\
|
r.db(dbname).table('bigchain')\
|
||||||
.index_create('block_timestamp', r.row['block']['timestamp'])\
|
.index_create('block_timestamp', r.row['block']['timestamp'])\
|
||||||
.run(conn)
|
.run(conn)
|
||||||
# to order blocks by block number
|
|
||||||
r.db(dbname).table('bigchain')\
|
|
||||||
.index_create('block_number', r.row['block']['block_number'])\
|
|
||||||
.run(conn)
|
|
||||||
# to query the bigchain for a transaction id
|
# to query the bigchain for a transaction id
|
||||||
r.db(dbname).table('bigchain')\
|
r.db(dbname).table('bigchain')\
|
||||||
.index_create('transaction_id',
|
.index_create('transaction_id',
|
||||||
|
@ -56,3 +56,7 @@ class MultipleVotesError(Exception):
|
|||||||
|
|
||||||
class GenesisBlockAlreadyExistsError(Exception):
|
class GenesisBlockAlreadyExistsError(Exception):
|
||||||
"""Raised when trying to create the already existing genesis block"""
|
"""Raised when trying to create the already existing genesis block"""
|
||||||
|
|
||||||
|
|
||||||
|
class CyclicBlockchainError(Exception):
|
||||||
|
"""Raised when there is a cycle in the blockchain"""
|
||||||
|
@ -615,3 +615,18 @@ def transform_create(tx):
|
|||||||
new_tx = create_tx(b.me, transaction['fulfillments'][0]['current_owners'], None, 'CREATE', payload=payload)
|
new_tx = create_tx(b.me, transaction['fulfillments'][0]['current_owners'], None, 'CREATE', payload=payload)
|
||||||
return new_tx
|
return new_tx
|
||||||
|
|
||||||
|
|
||||||
|
def is_genesis_block(block):
|
||||||
|
"""Check if the block is the genesis block.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
block (dict): the block to check
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the block is the genesis block, False otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# we cannot have empty blocks, there will always be at least one
|
||||||
|
# element in the list so we can safely refer to it
|
||||||
|
return block['block']['transactions'][0]['transaction']['operation'] == 'GENESIS'
|
||||||
|
|
||||||
|
@ -65,7 +65,6 @@ class Voter(object):
|
|||||||
self.q_validated_block = mp.Queue()
|
self.q_validated_block = mp.Queue()
|
||||||
self.q_voted_block = mp.Queue()
|
self.q_voted_block = mp.Queue()
|
||||||
self.v_previous_block_id = mp.Value(ctypes.c_char_p)
|
self.v_previous_block_id = mp.Value(ctypes.c_char_p)
|
||||||
self.v_previous_block_number = mp.Value(ctypes.c_uint64)
|
|
||||||
self.initialized = mp.Event()
|
self.initialized = mp.Event()
|
||||||
|
|
||||||
def feed_blocks(self):
|
def feed_blocks(self):
|
||||||
@ -105,18 +104,15 @@ class Voter(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
logger.info('new_block arrived to voter')
|
logger.info('new_block arrived to voter')
|
||||||
block_number = self.v_previous_block_number.value + 1
|
|
||||||
|
|
||||||
with self.monitor.timer('validate_block'):
|
with self.monitor.timer('validate_block'):
|
||||||
validity = b.is_valid_block(new_block)
|
validity = b.is_valid_block(new_block)
|
||||||
|
|
||||||
self.q_validated_block.put((new_block,
|
self.q_validated_block.put((new_block,
|
||||||
self.v_previous_block_id.value.decode(),
|
self.v_previous_block_id.value.decode(),
|
||||||
block_number,
|
|
||||||
validity))
|
validity))
|
||||||
|
|
||||||
self.v_previous_block_id.value = new_block['id'].encode()
|
self.v_previous_block_id.value = new_block['id'].encode()
|
||||||
self.v_previous_block_number.value = block_number
|
|
||||||
|
|
||||||
def vote(self):
|
def vote(self):
|
||||||
"""
|
"""
|
||||||
@ -134,9 +130,9 @@ class Voter(object):
|
|||||||
self.q_voted_block.put('stop')
|
self.q_voted_block.put('stop')
|
||||||
return
|
return
|
||||||
|
|
||||||
validated_block, previous_block_id, block_number, decision = elem
|
validated_block, previous_block_id, decision = elem
|
||||||
vote = b.vote(validated_block, previous_block_id, decision)
|
vote = b.vote(validated_block, previous_block_id, decision)
|
||||||
self.q_voted_block.put((validated_block, vote, block_number))
|
self.q_voted_block.put((validated_block, vote))
|
||||||
|
|
||||||
def update_block(self):
|
def update_block(self):
|
||||||
"""
|
"""
|
||||||
@ -154,22 +150,21 @@ class Voter(object):
|
|||||||
logger.info('clean exit')
|
logger.info('clean exit')
|
||||||
return
|
return
|
||||||
|
|
||||||
block, vote, block_number = elem
|
block, vote = elem
|
||||||
logger.info('updating block %s with number %s and with vote %s', block['id'], block_number, vote)
|
logger.info('updating block %s and with vote %s', block['id'], vote)
|
||||||
b.write_vote(block, vote, block_number)
|
b.write_vote(block, vote)
|
||||||
|
|
||||||
def bootstrap(self):
|
def bootstrap(self):
|
||||||
"""
|
"""
|
||||||
Before starting handling the new blocks received by the changefeed we need to handle unvoted blocks
|
Before starting handling the new blocks received by the changefeed we need to handle unvoted blocks
|
||||||
added to the bigchain while the process was down
|
added to the bigchain while the process was down
|
||||||
|
|
||||||
We also need to set the previous_block_id and the previous block_number
|
We also need to set the previous_block_id.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
b = Bigchain()
|
b = Bigchain()
|
||||||
last_voted = b.get_last_voted_block()
|
last_voted = b.get_last_voted_block()
|
||||||
|
|
||||||
self.v_previous_block_number.value = last_voted['block_number']
|
|
||||||
self.v_previous_block_id.value = last_voted['id'].encode()
|
self.v_previous_block_id.value = last_voted['id'].encode()
|
||||||
|
|
||||||
def kill(self):
|
def kill(self):
|
||||||
|
@ -270,7 +270,6 @@ Each node must generate a vote for each block, to be appended the `votes` table.
|
|||||||
"timestamp": "<Unix time when the vote was generated, provided by the voting node>"
|
"timestamp": "<Unix time when the vote was generated, provided by the voting node>"
|
||||||
},
|
},
|
||||||
"signature": "<signature of vote>",
|
"signature": "<signature of vote>",
|
||||||
"block_number": "<roughly sequential integer index for block ordering>"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -98,23 +98,22 @@ def cleanup_tables(request, node_config):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def inputs(user_vk, amount=1, b=None):
|
def inputs(user_vk):
|
||||||
from bigchaindb.exceptions import GenesisBlockAlreadyExistsError
|
from bigchaindb.exceptions import GenesisBlockAlreadyExistsError
|
||||||
# 1. create the genesis block
|
# 1. create the genesis block
|
||||||
b = b or Bigchain()
|
b = Bigchain()
|
||||||
try:
|
try:
|
||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
except GenesisBlockAlreadyExistsError:
|
except GenesisBlockAlreadyExistsError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 2. create block with transactions for `USER` to spend
|
# 2. create block with transactions for `USER` to spend
|
||||||
|
for block in range(4):
|
||||||
transactions = []
|
transactions = []
|
||||||
for i in range(amount):
|
for i in range(10):
|
||||||
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
|
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
|
||||||
tx_signed = b.sign_transaction(tx, b.me_private)
|
tx_signed = b.sign_transaction(tx, b.me_private)
|
||||||
transactions.append(tx_signed)
|
transactions.append(tx_signed)
|
||||||
b.write_transaction(tx_signed)
|
|
||||||
|
|
||||||
block = b.create_block(transactions)
|
block = b.create_block(transactions)
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
return block
|
|
||||||
|
@ -140,7 +140,7 @@ class TestBigchainApi(object):
|
|||||||
|
|
||||||
# vote the block invalid
|
# vote the block invalid
|
||||||
vote = b.vote(block, b.get_last_voted_block()['id'], False)
|
vote = b.vote(block, b.get_last_voted_block()['id'], False)
|
||||||
b.write_vote(block, vote, 3)
|
b.write_vote(block, vote)
|
||||||
response = b.get_transaction(tx_signed["id"])
|
response = b.get_transaction(tx_signed["id"])
|
||||||
|
|
||||||
# should be None, because invalid blocks are ignored
|
# should be None, because invalid blocks are ignored
|
||||||
@ -181,13 +181,14 @@ class TestBigchainApi(object):
|
|||||||
@pytest.mark.usefixtures('inputs')
|
@pytest.mark.usefixtures('inputs')
|
||||||
def test_genesis_block(self, b):
|
def test_genesis_block(self, b):
|
||||||
response = list(r.table('bigchain')
|
response = list(r.table('bigchain')
|
||||||
.filter(r.row['block_number'] == 0)
|
.filter(util.is_genesis_block)
|
||||||
.run(b.conn))[0]
|
.run(b.conn))
|
||||||
|
|
||||||
assert response['block_number'] == 0
|
assert len(response) == 1
|
||||||
assert len(response['block']['transactions']) == 1
|
block = response[0]
|
||||||
assert response['block']['transactions'][0]['transaction']['operation'] == 'GENESIS'
|
assert len(block['block']['transactions']) == 1
|
||||||
assert response['block']['transactions'][0]['transaction']['fulfillments'][0]['input'] is None
|
assert block['block']['transactions'][0]['transaction']['operation'] == 'GENESIS'
|
||||||
|
assert block['block']['transactions'][0]['transaction']['fulfillments'][0]['input'] is None
|
||||||
|
|
||||||
def test_create_genesis_block_fails_if_table_not_empty(self, b):
|
def test_create_genesis_block_fails_if_table_not_empty(self, b):
|
||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
@ -196,7 +197,7 @@ class TestBigchainApi(object):
|
|||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
|
|
||||||
genesis_blocks = list(r.table('bigchain')
|
genesis_blocks = list(r.table('bigchain')
|
||||||
.filter(r.row['block_number'] == 0)
|
.filter(util.is_genesis_block)
|
||||||
.run(b.conn))
|
.run(b.conn))
|
||||||
|
|
||||||
assert len(genesis_blocks) == 1
|
assert len(genesis_blocks) == 1
|
||||||
@ -257,13 +258,13 @@ class TestBigchainApi(object):
|
|||||||
def test_get_last_voted_block_returns_genesis_if_no_votes_has_been_casted(self, b):
|
def test_get_last_voted_block_returns_genesis_if_no_votes_has_been_casted(self, b):
|
||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
genesis = list(r.table('bigchain')
|
genesis = list(r.table('bigchain')
|
||||||
.filter(r.row['block_number'] == 0)
|
.filter(util.is_genesis_block)
|
||||||
.run(b.conn))[0]
|
.run(b.conn))[0]
|
||||||
gb = b.get_last_voted_block()
|
gb = b.get_last_voted_block()
|
||||||
assert gb == genesis
|
assert gb == genesis
|
||||||
assert b.validate_block(gb) == gb
|
assert b.validate_block(gb) == gb
|
||||||
|
|
||||||
def test_get_last_voted_block_returns_the_correct_block(self, b):
|
def test_get_last_voted_block_returns_the_correct_block_same_timestamp(self, b, monkeypatch):
|
||||||
genesis = b.create_genesis_block()
|
genesis = b.create_genesis_block()
|
||||||
|
|
||||||
assert b.get_last_voted_block() == genesis
|
assert b.get_last_voted_block() == genesis
|
||||||
@ -276,27 +277,57 @@ class TestBigchainApi(object):
|
|||||||
b.write_block(block_2, durability='hard')
|
b.write_block(block_2, durability='hard')
|
||||||
b.write_block(block_3, durability='hard')
|
b.write_block(block_3, durability='hard')
|
||||||
|
|
||||||
b.write_vote(block_1, b.vote(block_1, b.get_last_voted_block(), True), 1)
|
# make sure all the blocks are written at the same time
|
||||||
|
monkeypatch.setattr(util, 'timestamp', lambda: '1')
|
||||||
|
|
||||||
|
b.write_vote(block_1, b.vote(block_1, b.get_last_voted_block()['id'], True))
|
||||||
assert b.get_last_voted_block()['id'] == block_1['id']
|
assert b.get_last_voted_block()['id'] == block_1['id']
|
||||||
|
|
||||||
b.write_vote(block_2, b.vote(block_2, b.get_last_voted_block(), True), 2)
|
b.write_vote(block_2, b.vote(block_2, b.get_last_voted_block()['id'], True))
|
||||||
assert b.get_last_voted_block()['id'] == block_2['id']
|
assert b.get_last_voted_block()['id'] == block_2['id']
|
||||||
|
|
||||||
b.write_vote(block_3, b.vote(block_3, b.get_last_voted_block(), True), 3)
|
b.write_vote(block_3, b.vote(block_3, b.get_last_voted_block()['id'], True))
|
||||||
|
assert b.get_last_voted_block()['id'] == block_3['id']
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_last_voted_block_returns_the_correct_block_different_timestamps(self, b, monkeypatch):
|
||||||
|
genesis = b.create_genesis_block()
|
||||||
|
|
||||||
|
assert b.get_last_voted_block() == genesis
|
||||||
|
|
||||||
|
block_1 = dummy_block()
|
||||||
|
block_2 = dummy_block()
|
||||||
|
block_3 = dummy_block()
|
||||||
|
|
||||||
|
b.write_block(block_1, durability='hard')
|
||||||
|
b.write_block(block_2, durability='hard')
|
||||||
|
b.write_block(block_3, durability='hard')
|
||||||
|
|
||||||
|
# make sure all the blocks are written at different timestamps
|
||||||
|
monkeypatch.setattr(util, 'timestamp', lambda: '1')
|
||||||
|
b.write_vote(block_1, b.vote(block_1, b.get_last_voted_block()['id'], True))
|
||||||
|
assert b.get_last_voted_block()['id'] == block_1['id']
|
||||||
|
|
||||||
|
monkeypatch.setattr(util, 'timestamp', lambda: '2')
|
||||||
|
b.write_vote(block_2, b.vote(block_2, b.get_last_voted_block()['id'], True))
|
||||||
|
assert b.get_last_voted_block()['id'] == block_2['id']
|
||||||
|
|
||||||
|
monkeypatch.setattr(util, 'timestamp', lambda: '3')
|
||||||
|
b.write_vote(block_3, b.vote(block_3, b.get_last_voted_block()['id'], True))
|
||||||
assert b.get_last_voted_block()['id'] == block_3['id']
|
assert b.get_last_voted_block()['id'] == block_3['id']
|
||||||
|
|
||||||
def test_no_vote_written_if_block_already_has_vote(self, b):
|
def test_no_vote_written_if_block_already_has_vote(self, b):
|
||||||
b.create_genesis_block()
|
genesis = b.create_genesis_block()
|
||||||
|
|
||||||
block_1 = dummy_block()
|
block_1 = dummy_block()
|
||||||
|
|
||||||
b.write_block(block_1, durability='hard')
|
b.write_block(block_1, durability='hard')
|
||||||
|
|
||||||
b.write_vote(block_1, b.vote(block_1, b.get_last_voted_block(), True), 1)
|
b.write_vote(block_1, b.vote(block_1, genesis['id'], True))
|
||||||
retrieved_block_1 = r.table('bigchain').get(block_1['id']).run(b.conn)
|
retrieved_block_1 = r.table('bigchain').get(block_1['id']).run(b.conn)
|
||||||
|
|
||||||
# try to vote again on the retrieved block, should do nothing
|
# try to vote again on the retrieved block, should do nothing
|
||||||
b.write_vote(retrieved_block_1, b.vote(retrieved_block_1, b.get_last_voted_block(), True), 1)
|
b.write_vote(retrieved_block_1, b.vote(retrieved_block_1, genesis['id'], True))
|
||||||
retrieved_block_2 = r.table('bigchain').get(block_1['id']).run(b.conn)
|
retrieved_block_2 = r.table('bigchain').get(block_1['id']).run(b.conn)
|
||||||
|
|
||||||
assert retrieved_block_1 == retrieved_block_2
|
assert retrieved_block_1 == retrieved_block_2
|
||||||
@ -306,8 +337,8 @@ class TestBigchainApi(object):
|
|||||||
block_1 = dummy_block()
|
block_1 = dummy_block()
|
||||||
b.write_block(block_1, durability='hard')
|
b.write_block(block_1, durability='hard')
|
||||||
# insert duplicate votes
|
# insert duplicate votes
|
||||||
vote_1 = b.vote(block_1, b.get_last_voted_block(), True)
|
vote_1 = b.vote(block_1, b.get_last_voted_block()['id'], True)
|
||||||
vote_2 = b.vote(block_1, b.get_last_voted_block(), True)
|
vote_2 = b.vote(block_1, b.get_last_voted_block()['id'], True)
|
||||||
vote_2['node_pubkey'] = 'aaaaaaa'
|
vote_2['node_pubkey'] = 'aaaaaaa'
|
||||||
r.table('votes').insert(vote_1).run(b.conn)
|
r.table('votes').insert(vote_1).run(b.conn)
|
||||||
r.table('votes').insert(vote_2).run(b.conn)
|
r.table('votes').insert(vote_2).run(b.conn)
|
||||||
@ -319,12 +350,12 @@ class TestBigchainApi(object):
|
|||||||
.format(block_id=block_1['id'], n_votes=str(2), n_voters=str(1))
|
.format(block_id=block_1['id'], n_votes=str(2), n_voters=str(1))
|
||||||
|
|
||||||
def test_multiple_votes_single_node(self, b):
|
def test_multiple_votes_single_node(self, b):
|
||||||
b.create_genesis_block()
|
genesis = b.create_genesis_block()
|
||||||
block_1 = dummy_block()
|
block_1 = dummy_block()
|
||||||
b.write_block(block_1, durability='hard')
|
b.write_block(block_1, durability='hard')
|
||||||
# insert duplicate votes
|
# insert duplicate votes
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
r.table('votes').insert(b.vote(block_1, b.get_last_voted_block(), True)).run(b.conn)
|
r.table('votes').insert(b.vote(block_1, genesis['id'], True)).run(b.conn)
|
||||||
|
|
||||||
from bigchaindb.exceptions import MultipleVotesError
|
from bigchaindb.exceptions import MultipleVotesError
|
||||||
with pytest.raises(MultipleVotesError) as excinfo:
|
with pytest.raises(MultipleVotesError) as excinfo:
|
||||||
@ -341,7 +372,7 @@ class TestBigchainApi(object):
|
|||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
block_1 = dummy_block()
|
block_1 = dummy_block()
|
||||||
b.write_block(block_1, durability='hard')
|
b.write_block(block_1, durability='hard')
|
||||||
vote_1 = b.vote(block_1, b.get_last_voted_block(), True)
|
vote_1 = b.vote(block_1, b.get_last_voted_block()['id'], True)
|
||||||
# mangle the signature
|
# mangle the signature
|
||||||
vote_1['signature'] = 'a' * 87
|
vote_1['signature'] = 'a' * 87
|
||||||
r.table('votes').insert(vote_1).run(b.conn)
|
r.table('votes').insert(vote_1).run(b.conn)
|
||||||
@ -898,9 +929,9 @@ class TestBigchainBlock(object):
|
|||||||
b.write_block(block_2, durability='hard')
|
b.write_block(block_2, durability='hard')
|
||||||
b.write_block(block_3, durability='hard')
|
b.write_block(block_3, durability='hard')
|
||||||
|
|
||||||
b.write_vote(block_1, b.vote(block_1, b.get_last_voted_block(), True), 1)
|
b.write_vote(block_1, b.vote(block_1, b.get_last_voted_block()['id'], True))
|
||||||
b.write_vote(block_2, b.vote(block_2, b.get_last_voted_block(), True), 2)
|
b.write_vote(block_2, b.vote(block_2, b.get_last_voted_block()['id'], True))
|
||||||
b.write_vote(block_3, b.vote(block_3, b.get_last_voted_block(), True), 3)
|
b.write_vote(block_3, b.vote(block_3, b.get_last_voted_block()['id'], True))
|
||||||
|
|
||||||
q_revert_delete = mp.Queue()
|
q_revert_delete = mp.Queue()
|
||||||
|
|
||||||
@ -1186,6 +1217,7 @@ class TestMultipleInputs(object):
|
|||||||
assert owned_inputs_user2 == [{'cid': 0, 'txid': tx['id']}]
|
assert owned_inputs_user2 == [{'cid': 0, 'txid': tx['id']}]
|
||||||
|
|
||||||
def test_get_owned_ids_single_tx_single_output_invalid_block(self, b, user_sk, user_vk):
|
def test_get_owned_ids_single_tx_single_output_invalid_block(self, b, user_sk, user_vk):
|
||||||
|
genesis = b.create_genesis_block()
|
||||||
# create a new users
|
# create a new users
|
||||||
user2_sk, user2_vk = crypto.generate_key_pair()
|
user2_sk, user2_vk = crypto.generate_key_pair()
|
||||||
|
|
||||||
@ -1196,8 +1228,8 @@ class TestMultipleInputs(object):
|
|||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
|
|
||||||
# vote the block VALID
|
# vote the block VALID
|
||||||
vote = b.vote(block, b.get_unvoted_blocks()[0]['id'], True)
|
vote = b.vote(block, genesis['id'], True)
|
||||||
b.write_vote(block, vote, 2)
|
b.write_vote(block, vote)
|
||||||
|
|
||||||
# get input
|
# get input
|
||||||
owned_inputs_user1 = b.get_owned_ids(user_vk)
|
owned_inputs_user1 = b.get_owned_ids(user_vk)
|
||||||
@ -1213,7 +1245,7 @@ class TestMultipleInputs(object):
|
|||||||
|
|
||||||
# vote the block invalid
|
# vote the block invalid
|
||||||
vote = b.vote(block, b.get_last_voted_block()['id'], False)
|
vote = b.vote(block, b.get_last_voted_block()['id'], False)
|
||||||
b.write_vote(block, vote, 3)
|
b.write_vote(block, vote)
|
||||||
|
|
||||||
owned_inputs_user1 = b.get_owned_ids(user_vk)
|
owned_inputs_user1 = b.get_owned_ids(user_vk)
|
||||||
owned_inputs_user2 = b.get_owned_ids(user2_vk)
|
owned_inputs_user2 = b.get_owned_ids(user2_vk)
|
||||||
@ -1314,6 +1346,8 @@ class TestMultipleInputs(object):
|
|||||||
assert spent_inputs_user1 == tx_signed
|
assert spent_inputs_user1 == tx_signed
|
||||||
|
|
||||||
def test_get_spent_single_tx_single_output_invalid_block(self, b, user_sk, user_vk):
|
def test_get_spent_single_tx_single_output_invalid_block(self, b, user_sk, user_vk):
|
||||||
|
genesis = b.create_genesis_block()
|
||||||
|
|
||||||
# create a new users
|
# create a new users
|
||||||
user2_sk, user2_vk = crypto.generate_key_pair()
|
user2_sk, user2_vk = crypto.generate_key_pair()
|
||||||
|
|
||||||
@ -1324,8 +1358,8 @@ class TestMultipleInputs(object):
|
|||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
|
|
||||||
# vote the block VALID
|
# vote the block VALID
|
||||||
vote = b.vote(block, b.get_unvoted_blocks()[0]['id'], True)
|
vote = b.vote(block, genesis['id'], True)
|
||||||
b.write_vote(block, vote, 2)
|
b.write_vote(block, vote)
|
||||||
|
|
||||||
# get input
|
# get input
|
||||||
owned_inputs_user1 = b.get_owned_ids(user_vk)
|
owned_inputs_user1 = b.get_owned_ids(user_vk)
|
||||||
@ -1342,7 +1376,7 @@ class TestMultipleInputs(object):
|
|||||||
|
|
||||||
# vote the block invalid
|
# vote the block invalid
|
||||||
vote = b.vote(block, b.get_last_voted_block()['id'], False)
|
vote = b.vote(block, b.get_last_voted_block()['id'], False)
|
||||||
b.write_vote(block, vote, 2)
|
b.write_vote(block, vote)
|
||||||
response = b.get_transaction(tx_signed["id"])
|
response = b.get_transaction(tx_signed["id"])
|
||||||
spent_inputs_user1 = b.get_spent(owned_inputs_user1[0])
|
spent_inputs_user1 = b.get_spent(owned_inputs_user1[0])
|
||||||
|
|
||||||
@ -1988,6 +2022,7 @@ class TestCryptoconditions(object):
|
|||||||
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
@pytest.mark.usefixtures('inputs')
|
||||||
def test_transfer_asset_with_hashlock_condition(self, b, user_vk, user_sk):
|
def test_transfer_asset_with_hashlock_condition(self, b, user_vk, user_sk):
|
||||||
|
owned_count = len(b.get_owned_ids(user_vk))
|
||||||
first_input_tx = b.get_owned_ids(user_vk).pop()
|
first_input_tx = b.get_owned_ids(user_vk).pop()
|
||||||
|
|
||||||
hashlock_tx = b.create_transaction(user_vk, None, first_input_tx, 'TRANSFER')
|
hashlock_tx = b.create_transaction(user_vk, None, first_input_tx, 'TRANSFER')
|
||||||
@ -2010,7 +2045,7 @@ class TestCryptoconditions(object):
|
|||||||
|
|
||||||
assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
assert b.validate_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
||||||
assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
assert b.is_valid_transaction(hashlock_tx_signed) == hashlock_tx_signed
|
||||||
assert len(b.get_owned_ids(user_vk)) == 1
|
assert len(b.get_owned_ids(user_vk)) == owned_count
|
||||||
|
|
||||||
b.write_transaction(hashlock_tx_signed)
|
b.write_transaction(hashlock_tx_signed)
|
||||||
|
|
||||||
@ -2018,7 +2053,7 @@ class TestCryptoconditions(object):
|
|||||||
block = b.create_block([hashlock_tx_signed])
|
block = b.create_block([hashlock_tx_signed])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
|
|
||||||
assert len(b.get_owned_ids(user_vk)) == 0
|
assert len(b.get_owned_ids(user_vk)) == owned_count - 1
|
||||||
|
|
||||||
def test_create_and_fulfill_asset_with_hashlock_condition(self, b, user_vk):
|
def test_create_and_fulfill_asset_with_hashlock_condition(self, b, user_vk):
|
||||||
hashlock_tx = b.create_transaction(b.me, None, None, 'CREATE')
|
hashlock_tx = b.create_transaction(b.me, None, None, 'CREATE')
|
||||||
|
@ -30,8 +30,7 @@ def test_init_creates_db_tables_and_indexes():
|
|||||||
assert r.db(dbname).table_list().contains('backlog', 'bigchain').run(conn) is True
|
assert r.db(dbname).table_list().contains('backlog', 'bigchain').run(conn) is True
|
||||||
|
|
||||||
assert r.db(dbname).table('bigchain').index_list().contains(
|
assert r.db(dbname).table('bigchain').index_list().contains(
|
||||||
'block_timestamp',
|
'block_timestamp').run(conn) is True
|
||||||
'block_number').run(conn) is True
|
|
||||||
|
|
||||||
assert r.db(dbname).table('backlog').index_list().contains(
|
assert r.db(dbname).table('backlog').index_list().contains(
|
||||||
'transaction_timestamp',
|
'transaction_timestamp',
|
||||||
|
@ -325,11 +325,8 @@ class TestBigchainVoter(object):
|
|||||||
.run(b.conn))
|
.run(b.conn))
|
||||||
|
|
||||||
# retrieve votes
|
# retrieve votes
|
||||||
votes = r.table('votes')\
|
votes = list(r.table('votes').run(b.conn))
|
||||||
.order_by(r.asc((r.row['block_number'])))\
|
|
||||||
.run(b.conn)
|
|
||||||
|
|
||||||
assert blocks[0]['block_number'] == 0 # genesis block
|
|
||||||
assert votes[0]['vote']['voting_for_block'] in (blocks[1]['id'], blocks[2]['id'])
|
assert votes[0]['vote']['voting_for_block'] in (blocks[1]['id'], blocks[2]['id'])
|
||||||
assert votes[1]['vote']['voting_for_block'] in (blocks[1]['id'], blocks[2]['id'])
|
assert votes[1]['vote']['voting_for_block'] in (blocks[1]['id'], blocks[2]['id'])
|
||||||
|
|
||||||
|
@ -210,3 +210,9 @@ def test_check_hash_and_signature_invalid_signature(monkeypatch):
|
|||||||
'bigchaindb.util.validate_fulfillments', lambda tx: False)
|
'bigchaindb.util.validate_fulfillments', lambda tx: False)
|
||||||
with pytest.raises(InvalidSignature):
|
with pytest.raises(InvalidSignature):
|
||||||
check_hash_and_signature(transaction)
|
check_hash_and_signature(transaction)
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_genesis_block_returns_true_if_genesis(b):
|
||||||
|
from bigchaindb.util import is_genesis_block
|
||||||
|
genesis_block = b.prepare_genesis_block()
|
||||||
|
assert is_genesis_block(genesis_block)
|
||||||
|
@ -197,7 +197,7 @@ def test_update_config(monkeypatch):
|
|||||||
'database': {'host': 'test-host', 'name': 'bigchaindb', 'port': 28015}
|
'database': {'host': 'test-host', 'name': 'bigchaindb', 'port': 28015}
|
||||||
}
|
}
|
||||||
monkeypatch.setattr('bigchaindb.config_utils.file_config', lambda *args, **kwargs: file_config)
|
monkeypatch.setattr('bigchaindb.config_utils.file_config', lambda *args, **kwargs: file_config)
|
||||||
config_utils.autoconfigure()
|
config_utils.autoconfigure(config=file_config)
|
||||||
|
|
||||||
# update configuration, retaining previous changes
|
# update configuration, retaining previous changes
|
||||||
config_utils.update_config({'database': {'port': 28016, 'name': 'bigchaindb_other'}})
|
config_utils.update_config({'database': {'port': 28016, 'name': 'bigchaindb_other'}})
|
||||||
@ -205,4 +205,3 @@ def test_update_config(monkeypatch):
|
|||||||
assert bigchaindb.config['database']['host'] == 'test-host'
|
assert bigchaindb.config['database']['host'] == 'test-host'
|
||||||
assert bigchaindb.config['database']['name'] == 'bigchaindb_other'
|
assert bigchaindb.config['database']['name'] == 'bigchaindb_other'
|
||||||
assert bigchaindb.config['database']['port'] == 28016
|
assert bigchaindb.config['database']['port'] == 28016
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user