Exemple #1
0
    def test_timeout_over(self):
        # create a blocktree and add blocks to it
        b1 = Block(1, GENESIS.block_id, [Transaction(1, 'a', 1)], 1)
        b2 = Block(2, GENESIS.block_id, [Transaction(1, 'a', 2)], 2)
        b3 = Block(3, b2.block_id, [Transaction(1, 'a', 3)], 3)
        b4 = Block(4, b3.block_id, [Transaction(1, 'a', 4)], 4)
        b5 = Block(5, b2.block_id, [Transaction(1, 'a', 5)], 5)

        self.node.blocktree.add_block(b1)
        self.node.blocktree.add_block(b2)
        self.node.blocktree.add_block(b3)
        self.node.blocktree.add_block(b4)
        self.node.blocktree.add_block(b5)

        self.node.blocktree.head_block = b4

        txn = Transaction(1, 'a', 1)
        self.node.new_txs = [txn]
        self.node.broadcast = MagicMock()
        self.node.state = 0

        clock = task.Clock()
        self.node.reactor = clock
        self.node.timeout_over(txn)
        clock.advance(50)

        assert self.node.broadcast.called
        obj = self.node.broadcast.call_args[0][0]
        assert obj.last_committed_block == self.node.blocktree.committed_block.block_id
Exemple #2
0
    def test_valid_block(self):
        # create a blocktree and add blocks to it
        bt = Blocktree(0)
        bt.db = MagicMock()
        b1 = Block(1, GENESIS.block_id, [Transaction(0, 'c', 0)], 1)
        b2 = Block(2, GENESIS.block_id, [Transaction(0, 'c', 1)], 2)
        b3 = Block(3, b2.block_id, [Transaction(0, 'c', 2)], 3)
        b4 = Block(4, b3.block_id, [Transaction(0, 'c', 3)], 4)
        b5 = Block(5, b2.block_id, [Transaction(0, 'c', 4)], 5)

        bt.add_block(b1)
        bt.add_block(b2)
        bt.add_block(b3)
        bt.add_block(b4)
        bt.add_block(b5)

        bt.committed_block = b2
        bt.head_block = b3
        assert bt.valid_block(b4)

        bt.committed_block = b1
        bt.head_block = b1
        assert not bt.valid_block(b3)

        bt.committed_block = b3
        bt.head_block = b4
        assert not bt.valid_block(b3)
        assert not bt.valid_block(b2)
Exemple #3
0
    def test_block_set(self):
        """Test that __hash__ and __eq__ were implemented correctly."""
        b1 = Block(0, GENESIS.block_id, ['a'], 1)
        block_set = set()
        block_set.add(b1)
        block_set.add(b1)

        assert len(block_set) == 1

        b2 = Block(0, GENESIS.block_id, ['a'], 2)
        block_set.add(b2)

        assert len(block_set) == 2
Exemple #4
0
    def test_receive_paxos_message_try(self):
        # try message
        try_msg = PaxosMessage('TRY', 1)
        try_msg.last_committed_block = GENESIS.block_id

        b = Block(1, GENESIS.block_id, ['a'], 1)
        b.depth = 1
        try_msg.new_block = b.block_id

        self.node.respond = MagicMock()
        self.node.blocktree.nodes.update({b.block_id: b})

        self.node.receive_paxos_message(try_msg, 1)
        assert self.node.respond.called
        assert self.node.s_max_block_depth == b.depth
Exemple #5
0
    def test_receive_paxos_message_propose(self):
        propose = PaxosMessage('PROPOSE', 1)

        b = Block(1, GENESIS.block_id, ['a'], 1)
        b.depth = 1

        propose.new_block = GENESIS.block_id
        propose.com_block = GENESIS.block_id

        self.node.respond = MagicMock()
        self.node.receive_paxos_message(propose, 1)

        assert self.node.respond.called
        assert self.node.s_prop_block.block_id == propose.com_block
        assert self.node.s_supp_block.block_id == propose.new_block
Exemple #6
0
    def test_blk(self):
        """Test receipt of a Block.
        """
        self.node.receive_block = MagicMock()

        txn1 = Transaction(0, 'command1', 1)
        txn2 = Transaction(0, 'command2', 2)
        block = Block(0, 0, [txn1, txn2], 1)
        s = block.serialize()
        self.proto.stringReceived(s)

        self.assertTrue(self.node.receive_block.called)
        obj = self.node.receive_block.call_args[0][0]
        self.assertEqual(type(obj), Block)
        self.assertEqual(obj.txs[0], txn1)
Exemple #7
0
    def parse_msg(self, msg_type, msg, sender):

        if msg_type != 'PON':
            logger.debug('parse_msg called with msg_type = %s', msg_type)
        if msg_type == 'RQB':
            obj = RequestBlockMessage.unserialize(msg)
            self.receive_request_blocks_message(obj, sender)
        elif msg_type == 'TXN':
            obj = Transaction.unserialize(msg)
            self.receive_transaction(obj)
        elif msg_type == 'BLK':
            obj = Block.unserialize(msg)
            self.receive_block(obj)
        elif msg_type == 'RSB':
            obj = RespondBlockMessage.unserialize(msg)
            self.receive_respond_blocks_message(obj)
        elif msg_type == 'PAM':
            obj = PaxosMessage.unserialize(msg)
            self.receive_paxos_message(obj, sender)
        elif msg_type == 'PON':
            obj = PongMessage.unserialize(msg)
            self.receive_pong_message(obj, sender.peer_node_id)
        elif msg_type == 'ACM':
            obj = AckCommitMessage.unserialize(msg)
            self.receive_ack_commit_message(obj)
Exemple #8
0
    def test_receive_paxos_message_try_ok_1(self):
        # try_ok message with no prop/supp block stored locally and message does not contain a propose block
        try_ok = PaxosMessage('TRY_OK', 1)

        b = Block(1, GENESIS.block_id, ['a'], 1)
        b.depth = 1

        self.node.c_request_seq = 1
        self.node.c_new_block = b
        self.node.c_votes = 5
        self.node.broadcast = MagicMock()
        self.node.receive_paxos_message(try_ok, None)
        self.node.blocktree.nodes.update({b.block_id: b})

        assert self.node.broadcast.called
        assert self.node.c_com_block == self.node.c_new_block
Exemple #9
0
    def test_common_ancestor(self):
        # create a blocktree and add blocks to it
        bt = Blocktree(0)
        bt.db = MagicMock()
        b1 = Block(1, GENESIS.block_id, [Transaction(0, 'c', 0)], 1)
        b2 = Block(2, GENESIS.block_id, [Transaction(0, 'c', 1)], 2)
        b3 = Block(3, b2.block_id, [Transaction(0, 'c', 2)], 3)
        b4 = Block(4, b3.block_id, [Transaction(0, 'c', 3)], 4)
        b5 = Block(5, b2.block_id, [Transaction(0, 'c', 4)], 5)

        bt.add_block(b1)
        bt.add_block(b2)
        bt.add_block(b3)
        bt.add_block(b4)
        bt.add_block(b5)

        assert bt.common_ancestor(b1, b2) == GENESIS
        assert bt.common_ancestor(b4, b5) == b2
        assert bt.common_ancestor(b3, b4) == b3
Exemple #10
0
    def test_receive_request_blocks_message(self):
        b1 = Block(1, GENESIS.block_id, [Transaction(1, 'a', 1)], 1)
        b2 = Block(2, GENESIS.block_id, [Transaction(1, 'a', 2)], 2)
        b3 = Block(3, b2.block_id, [Transaction(1, 'a', 3)], 3)
        b4 = Block(4, b3.block_id, [Transaction(1, 'a', 4)], 4)
        b5 = Block(5, b2.block_id, [Transaction(1, 'a', 5)], 5)

        self.node.blocktree.add_block(b1)
        self.node.blocktree.add_block(b2)
        self.node.blocktree.add_block(b3)
        self.node.blocktree.add_block(b4)
        self.node.blocktree.add_block(b5)

        req = RequestBlockMessage(b4.block_id)

        self.node.respond = MagicMock()
        self.node.receive_request_blocks_message(req, None)

        assert self.node.respond.called
Exemple #11
0
    def test_receive_paxos_message_propose_ack(self):
        propose_ack = PaxosMessage('PROPOSE_ACK', 1)

        b = Block(1, GENESIS.block_id, ['a'], 1)
        b.depth = 1

        propose_ack.com_block = b.block_id

        self.node.c_request_seq = 1
        self.node.c_votes = 5
        self.node.blocktree.nodes.update({b.block_id: b})

        self.node.broadcast = MagicMock()
        self.node.commit = MagicMock()
        self.node.receive_paxos_message(propose_ack, None)

        assert self.node.broadcast.called

        obj = self.node.broadcast.call_args[0][0]
        assert obj.com_block == propose_ack.com_block
Exemple #12
0
    def test_rsp(self):
        """Test receipt of a RespondBlockMessage.
        """
        self.node.receive_respond_blocks_message = MagicMock()

        txn1 = Transaction(0, 'command1', 1)
        txn2 = Transaction(0, 'command2', 2)
        block = Block(0, 0, [txn1, txn2], 1)

        txn3 = Transaction(0, 'command3', 3)
        block2 = Block(1, 1, [txn1, txn3], 2)

        rsb = RespondBlockMessage([block, block2])

        s = rsb.serialize()
        self.proto.stringReceived(s)

        self.assertTrue(self.node.receive_respond_blocks_message.called)
        obj = self.node.receive_respond_blocks_message.call_args[0][0]
        self.assertEqual(type(obj), RespondBlockMessage)
        self.assertEqual(obj.blocks[0].txs[0], txn1)
Exemple #13
0
    def test_create_block(self):
        # create a blocktree and add blocks to it
        b1 = Block(1, GENESIS.block_id, [Transaction(1, 'a', 1)], 1)
        b2 = Block(2, GENESIS.block_id, [Transaction(1, 'a', 2)], 2)
        b3 = Block(3, b2.block_id, [Transaction(1, 'a', 3)], 3)
        b4 = Block(4, b3.block_id, [Transaction(1, 'a', 4)], 4)
        b5 = Block(5, b2.block_id, [Transaction(1, 'a', 5)], 5)

        self.node.blocktree.add_block(b1)
        self.node.blocktree.add_block(b2)
        self.node.blocktree.add_block(b3)
        self.node.blocktree.add_block(b4)
        self.node.blocktree.add_block(b5)

        self.node.blocktree.head_block = b4

        self.node.new_txs = [Transaction(1, 'a', 6)]

        c = self.node.create_block()
        assert len(self.node.new_txs) == 0
        assert self.node.blocktree.nodes.get(c.block_id) == c
Exemple #14
0
    def test_move_to_block(self):
        b1 = Block(1, GENESIS.block_id, [Transaction(1, 'a', 1)], 1)
        b2 = Block(2, GENESIS.block_id, [Transaction(2, 'a', 2)], 2)
        b3 = Block(3, b2.block_id, [Transaction(3, 'a', 3)], 3)
        b4 = Block(4, b3.block_id, [Transaction(4, 'a', 4)], 4)
        b5 = Block(5, b2.block_id, [Transaction(5, 'a', 5)], 5)
        b6 = Block(6, b4.block_id, [Transaction(6, 'a', 6)], 6)

        self.node.blocktree.add_block(b1)
        self.node.blocktree.add_block(b2)
        self.node.blocktree.add_block(b3)
        self.node.blocktree.add_block(b4)
        self.node.blocktree.add_block(b5)
        self.node.blocktree.add_block(b6)

        self.node.blocktree.head_block = b4

        old = self.node.blocktree.head_block
        # should both have no effect
        self.node.move_to_block(b3)
        self.node.move_to_block(b4)
        assert self.node.blocktree.head_block == old

        self.node.move_to_block(b6)
        assert self.node.blocktree.head_block == b6

        self.node.broadcast = MagicMock()
        self.node.move_to_block(b1)

        assert self.node.broadcast.called
        assert self.node.blocktree.head_block == b1
Exemple #15
0
    def test_pam(self):
        """Test receipt of a PaxosMessage.
        """
        self.node.receive_paxos_message = MagicMock()

        txn1 = Transaction(0, 'command1', 1)
        txn2 = Transaction(0, 'command2', 2)
        block = Block(0, 0, [txn1, txn2], 1)

        txn3 = Transaction(0, 'command3', 3)
        block2 = Block(1, 1, [txn1, txn3], 2)

        pam = PaxosMessage('TRY', 2)
        pam.new_block = block.block_id
        pam.last_committed_block = block2.block_id

        s = pam.serialize()
        self.proto.stringReceived(s)

        self.assertTrue(self.node.receive_paxos_message.called)
        obj = self.node.receive_paxos_message.call_args[0][0]
        self.assertEqual(type(obj), PaxosMessage)
        self.assertEqual(obj.new_block, block.block_id)
        self.assertEqual(obj.last_committed_block, block2.block_id)
Exemple #16
0
    def __init__(self, node_index):
        self.genesis = GENESIS
        self.head_block = GENESIS
        self.committed_block = GENESIS
        self.committed_blocks = [GENESIS.block_id]
        self.nodes = {}
        self.nodes.update({GENESIS.block_id: GENESIS})
        self.counter = 0
        self.ack_commits = {}

        # create a db instance (s.t blocks can be recovered after a crash)
        base_path = os.path.expanduser('~/.pichain')
        path = base_path + '/node_' + str(node_index)
        if not os.path.exists(path):
            os.makedirs(path)
        self.db = plyvel.DB(path, create_if_missing=True)

        # first load all the blocks
        for key, value in self.db:
            # block_id -> block
            if key.decode().isdigit():
                block_id = int(key.decode())
                block = Block.unserialize(value)
                self.nodes.update({block_id: block})

        # load all block ids and counter
        for key, value in self.db:
            if key == b'committed_block':
                block = self.nodes.get(int(value.decode()))
                self.committed_block = block
            elif key == b'head_block':
                block = self.nodes.get(int(value.decode()))
                self.head_block = block
            elif key == b'counter':
                self.counter = int(value.decode())
            elif key == b'genesis':
                block = self.nodes.get(int(value.decode()))
                self.genesis = block
            elif key == b'committed_blocks':
                block_ids = json.loads(value.decode())
                self.committed_blocks = block_ids
Exemple #17
0
    def create_block(self):
        """Create a block containing `new_txs` and return it.

        Returns:
            Block: The block that was created.

        """
        logger.debug('create a block')
        # store depth of current head_block (will be parent of new block)
        d = self.blocktree.head_block.depth

        # create block
        self.blocktree.counter += 1
        if len(self.new_txs) < MAX_TXN_COUNT:
            b = Block(self.id, self.blocktree.head_block.block_id,
                      self.new_txs, self.blocktree.counter)
            # create a new, empty list (do not use clear!)
            self.new_txs = []
        else:
            logger.debug(
                'Cannot fit all transactions in the block that is beeing created. Remaining transactions '
                'will be included in the next block.')
            txns_include = self.new_txs[:MAX_TXN_COUNT]
            b = Block(self.id, self.blocktree.head_block.block_id,
                      txns_include, self.blocktree.counter)
            self.new_txs = self.new_txs[MAX_TXN_COUNT:]
            self.readjust_timeout()

        # compute its depth (will be fixed -> depth field is only set once)
        b.depth = d + len(b.txs)

        self.blocktree.db.put(b'counter', str(self.blocktree.counter).encode())

        # add block to blocktree
        self.blocktree.add_block(b)

        # promote node
        if self.state != QUICK:
            self.state = max(QUICK, self.state - 1)
            logger.debug('Got promoted. State = %s', str(self.state))

        # add state of creator node to block
        b.creator_state = self.state

        logger.debug('created block with block id = %s', str(b.block_id))

        return b
Exemple #18
0
    def test_reach_genesis_block(self):

        b1 = Block(1, GENESIS.block_id, [Transaction(1, 'a', 1)], 1)
        b2 = Block(2, GENESIS.block_id, [Transaction(1, 'a', 2)], 2)
        b3 = Block(3, b2.block_id, [Transaction(1, 'a', 3)], 3)
        b4 = Block(4, b3.block_id, [Transaction(1, 'a', 4)], 4)
        b5 = Block(5, b2.block_id, [Transaction(1, 'a', 5)], 5)

        self.node.blocktree.add_block(b1)
        self.node.blocktree.add_block(b2)
        self.node.blocktree.add_block(b3)
        self.node.blocktree.add_block(b4)
        self.node.blocktree.add_block(b5)

        assert self.node.reach_genesis_block(b5)

        b = Block(1, 1234, [Transaction(1, 'a', 6)], 6)

        self.node.broadcast = MagicMock()
        self.node.reach_genesis_block(b)

        assert self.node.broadcast.called
Exemple #19
0
from piChain.PaxosNetwork import ConnectionManager
from piChain.blocktree import Blocktree
from piChain.messages import PaxosMessage, Block, RequestBlockMessage, RespondBlockMessage, Transaction, \
    AckCommitMessage
from piChain.config import ACCUMULATION_TIME, MAX_COMMIT_TIME, MAX_TXN_COUNT, TESTING, RECOVERY_BLOCKS_COUNT

# variables representing the state of a node
QUICK = 0
MEDIUM = 1
SLOW = 2

EPSILON = 0.001

# genesis block
GENESIS = Block(-1, None, [], 0)
GENESIS.depth = 0

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
if not TESTING:
    logging.disable(logging.DEBUG)


class Node(ConnectionManager):
    """This class represents a piChain node. It is a subclass of the ConnectionManager class defined in the networking
    module. This allows to directly call functions like broadcast and respond from the networking module and to override
    the receive-methods which are called in the networking module based on the type of the message.

    Args:
        node_index (int): the index of this node into the peers dictionary. The entry defines its ip address and port.
Exemple #20
0
    def test_write(self):

        b1 = Block(1, GENESIS.block_id, [Transaction(0, 'c', 0)], 1)
        b2 = Block(2, GENESIS.block_id, [Transaction(0, 'c', 1)], 2)
        b3 = Block(3, b2.block_id, [Transaction(0, 'c', 2)], 3)
        b4 = Block(4, b3.block_id, [Transaction(0, 'c', 3)], 4)
        b5 = Block(5, b2.block_id, [Transaction(0, 'c', 4)], 5)

        self.bt.add_block(b1)
        self.bt.add_block(b2)
        self.bt.add_block(b3)
        self.bt.add_block(b4)
        self.bt.add_block(b5)

        assert self.bt.db.get(str(b1.block_id).encode()) == b1.serialize()
        assert self.bt.db.get(str(b2.block_id).encode()) == b2.serialize()
        assert self.bt.db.get(str(b3.block_id).encode()) == b3.serialize()
        assert self.bt.db.get(str(b4.block_id).encode()) == b4.serialize()
        assert self.bt.db.get(str(b5.block_id).encode()) == b5.serialize()
Exemple #21
0
    def test_read(self):
        b1 = Block(1, GENESIS.block_id, [Transaction(0, 'c', 0)], 1)
        b2 = Block(2, GENESIS.block_id, [Transaction(0, 'c', 1)], 2)
        b3 = Block(3, b2.block_id, [Transaction(0, 'c', 2)], 3)
        b4 = Block(4, b3.block_id, [Transaction(0, 'c', 3)], 4)
        b5 = Block(5, b2.block_id, [Transaction(0, 'c', 4)], 5)

        self.bt.add_block(b1)
        self.bt.add_block(b2)
        self.bt.add_block(b3)
        self.bt.add_block(b4)
        self.bt.add_block(b5)

        self.bt.db.close()

        # create another BLocktree wich should load the blocks stored by self.bt
        bt2 = Blocktree(0)

        assert bt2.db.get(str(b1.block_id).encode()) == b1.serialize()
        assert bt2.db.get(str(b2.block_id).encode()) == b2.serialize()
        assert bt2.db.get(str(b3.block_id).encode()) == b3.serialize()
        assert bt2.db.get(str(b4.block_id).encode()) == b4.serialize()
        assert bt2.db.get(str(b5.block_id).encode()) == b5.serialize()

        bt2.db.close()