93 lines
4.2 KiB
Python
93 lines
4.2 KiB
Python
import logging
|
|
import unittest
|
|
from datetime import datetime
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
from freezegun import freeze_time
|
|
|
|
from aucoin import consensus, dsa, util
|
|
from aucoin.block import Block
|
|
from aucoin.blockchain import Blockchain
|
|
from aucoin.database import session_scope
|
|
from aucoin.exceptions import InvalidBlockException, OrphanException
|
|
from aucoin.mempool import Mempool
|
|
from aucoin.network import Network
|
|
from aucoin.transactions import CoinbaseTransaction
|
|
from aucoin.validation import Validator
|
|
from aucoin.wallet import public_bytes
|
|
from tests import helpers
|
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
easy_target = bytes.fromhex("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
|
|
|
|
|
|
@patch("aucoin.consensus.required_target", MagicMock(return_value=easy_target))
|
|
class TestAddBlock(unittest.TestCase):
|
|
def setUp(self):
|
|
# setup a fresh blockchain and mempool
|
|
self.blockchain = Blockchain(reset=True)
|
|
self.mempool = Mempool()
|
|
self.network = Network(self.blockchain, self.mempool, max_peers=0)
|
|
|
|
# A valid block used for testing.
|
|
with session_scope() as session:
|
|
self.private_key, self.public_key = dsa.generate_keypair()
|
|
self.block = Block(
|
|
target=easy_target,
|
|
hash_prev_block=self.blockchain.genesis_block(session).hash,
|
|
public_key=public_bytes(self.public_key),
|
|
transactions=[
|
|
CoinbaseTransaction(
|
|
address=util.address(public_bytes(self.public_key)),
|
|
block_height=1
|
|
)
|
|
]
|
|
)
|
|
|
|
self.validator = Validator(helpers.Core(), self.blockchain, self.mempool, self.network)
|
|
|
|
def test_valid(self):
|
|
self.validator.add_block(helpers.mine(self.block, self.private_key))
|
|
|
|
def test_reject_invalid_syntax(self):
|
|
self.block.transactions = []
|
|
self.assertRaisesRegex(InvalidBlockException, "Transaction list must be non-empty",
|
|
self.validator.add_block, helpers.mine(self.block, self.private_key))
|
|
|
|
def test_reject_duplicate(self):
|
|
self.block = helpers.mine(self.block, self.private_key)
|
|
self.validator.add_block(self.block)
|
|
self.assertRaisesRegex(InvalidBlockException, "Already exists in blockchain",
|
|
self.validator.add_block, self.block)
|
|
|
|
def test_reject_orphan(self):
|
|
self.block.hash_prev_block = b"non existent"
|
|
|
|
with self.assertRaises(OrphanException) as cm:
|
|
self.validator.add_block(helpers.mine(self.block, self.private_key))
|
|
self.assertEqual(cm.exception.missing, b'non existent')
|
|
|
|
def test_reject_target_difficulty_rules_mismatch(self):
|
|
self.block.target = bytes.fromhex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
|
|
self.assertRaisesRegex(InvalidBlockException, "Target does not match the difficulty rules",
|
|
self.validator.add_block, helpers.mine(self.block, self.private_key))
|
|
|
|
@freeze_time("2010-01-01")
|
|
def test_reject_future_timestamp(self):
|
|
self.block.timestamp = int((datetime(2010, 1, 1, 0, 0, 1) + consensus.block_max_future_time).timestamp())
|
|
self.assertRaisesRegex(InvalidBlockException, "Block timestamp must not be more than block_max_future_time in the future",
|
|
self.validator.add_block, helpers.mine(self.block, self.private_key))
|
|
|
|
def test_reject_old_timestamp(self):
|
|
with session_scope() as session:
|
|
self.block.timestamp = self.blockchain.genesis_block(session).timestamp
|
|
self.assertRaisesRegex(InvalidBlockException, "Timestamp is before or equal to median time of the last n blocks",
|
|
self.validator.add_block, helpers.mine(self.block, self.private_key))
|
|
|
|
def test_reject_incorrect_block_height(self):
|
|
self.block.transactions[0].inputs[0].block_height = 3
|
|
self.block.calculate_merkle()
|
|
self.assertRaisesRegex(InvalidBlockException, "Block height is not equal to previous block's height \+ 1",
|
|
self.validator.add_block, helpers.mine(self.block, self.private_key))
|