Revision 367516589fc30cad7fbbed6fb157e6bfa62753bc authored by Marge Bot on 05 December 2022, 14:07:13 UTC, committed by Marge Bot on 05 December 2022, 14:07:13 UTC
Co-authored-by: iguerNL@Functori <iguer@functori.com>

Approved-by: Rodi-Can Bozman <rbozman.functori@gmail.com>
Approved-by: Martin Tomazic <martin.tomazic97@gmail.com>
Approved-by: Rémy El Sibaïe <remy.el-sibaie@nomadic-labs.com>
Approved-by: Mohamed IGUERNLALA <iguer@functori.com>
Approved-by: François Thiré <francois.thire@nomadic-labs.com>

See merge request https://gitlab.com/tezos/tezos/-/merge_requests/7046
2 parent s cc4ea3c + 767ba71
Raw File
test_nonce_seed_revelation.py
import time
import pytest
from tools import constants
from launchers.sandbox import Sandbox
from . import protocol


BLOCKS_PER_COMMITMENT = protocol.PARAMETERS['blocks_per_commitment']
BLOCKS_PER_CYCLE = protocol.PARAMETERS['blocks_per_cycle']
FIRST_PROTOCOL_BLOCK = 1
TIMEOUT = 60

MINIMAL_BLOCK_DELAY = 1
DELAY_INCREMENT_PER_ROUND = 1
TEST_DURATION = (
    FIRST_PROTOCOL_BLOCK + 2 * BLOCKS_PER_CYCLE
) * MINIMAL_BLOCK_DELAY
NUM_NODES = 5


@pytest.mark.incremental
@pytest.mark.slow
@pytest.mark.baker
class TestNonceSeedRevelation:
    """Test baker injection of nonce revelations.

    See http://tezos.gitlab.io/014_kathmandu/proof_of_stake.html

    Runs a node and a baker. The baker bakes two full cycles.
    We collect nonce hashes from the first cycle. And check
    that they are revealed in the second cycle"""

    def test_init(self, sandbox: Sandbox):
        """Run a node and a baker.

        The node runs in archive mode to get metadata in `client.get_block()`.
        The protocol is activated in the past so the baker can submit blocks
        immediately without waiting for current time."""

        node_params = constants.NODE_PARAMS + ['--history-mode', 'archive']
        for i in range(NUM_NODES):
            sandbox.add_node(i, params=node_params)

        # client setup
        parameters = protocol.get_parameters()
        parameters['minimal_block_delay'] = str(MINIMAL_BLOCK_DELAY)
        parameters['delay_increment_per_round'] = str(DELAY_INCREMENT_PER_ROUND)
        protocol.activate(sandbox.client(0), parameters=parameters)

        # baker setup
        # delegated_accounts = [f'bootstrap{i}' for i in range(1, 6)]
        for i in range(NUM_NODES):
            sandbox.add_baker(
                i,
                [f"bootstrap{i + 1}"],
                proto=protocol.DAEMON,
                log_levels=constants.TENDERBAKE_BAKER_LOG_LEVELS,
                run_params=['--liquidity-baking-toggle-vote', 'pass'],
            )

    @pytest.mark.timeout(2 * TEST_DURATION)
    def test_wait_for_two_cycles(self, sandbox: Sandbox):
        """Poll the node until target level is reached"""
        target = FIRST_PROTOCOL_BLOCK + 2 * BLOCKS_PER_CYCLE
        level = target - 1
        while level < target:
            time.sleep(
                4 * MINIMAL_BLOCK_DELAY
            )  # sleep first to avoid useless first query
            if sandbox.client(0).get_level() >= target:
                break
        # No need to bake more
        for i in range(NUM_NODES):
            sandbox.rm_baker(i, proto=protocol.DAEMON)

    def test_get_all_blocks(self, sandbox: Sandbox, session: dict):
        """Retrieve all blocks for two full cycles."""
        blocks = [
            sandbox.client(0).get_block(FIRST_PROTOCOL_BLOCK + i)
            for i in range(2 * BLOCKS_PER_CYCLE)
        ]
        session['blocks'] = blocks

    def test_cycle_alignment(self, session):
        """Test cycles start where they are supposed to start.

        Not really needed but helps clarifying cycles positions."""

        blocks = session['blocks']
        # blocks[0] is considered cycle = 0, cycle_position = 0 for the new
        # protocol, but because it is a protocol transition block, it
        # doesn't have the "cycle" and "cycle_position" metadata (unlike
        # the remaining blocks)
        initial_block_level = blocks[1]['metadata']['level_info']
        assert initial_block_level['cycle'] == 0
        assert initial_block_level['cycle_position'] == 1
        final_block_level = blocks[BLOCKS_PER_CYCLE]['metadata']['level_info']
        assert final_block_level['cycle'] == 1
        assert final_block_level['cycle_position'] == 0

    def test_collect_seed_nonce_hashes(self, session):
        """Collect nonce hashes in the block headers in the first cycle"""
        seed_nonce_hashes = {}
        blocks = session['blocks']
        for i in range(BLOCKS_PER_CYCLE // BLOCKS_PER_COMMITMENT):
            level = (i + 1) * BLOCKS_PER_COMMITMENT - 1
            seed_nonce_hash = blocks[level]['header']['seed_nonce_hash']
            seed_nonce_hashes[level] = seed_nonce_hash
        session['seed_nonce_hashes'] = seed_nonce_hashes

    def test_check_revelations(self, session):
        """Collect reveal ops in second cycle and check they match
        the nonce hashes from first cycle."""
        blocks = session['blocks']
        seed_nonce_hashes = session['seed_nonce_hashes']
        ops = []
        # collect all operations
        for i in range(BLOCKS_PER_CYCLE, 2 * BLOCKS_PER_CYCLE):
            ops.extend(blocks[i]['operations'][2])
        reveal_ops = {}
        for operation in ops:
            content = operation['contents'][0]
            # there should be only revelations there
            assert content['kind'] == "seed_nonce_revelation"
            level = content['level'] - FIRST_PROTOCOL_BLOCK
            # Can't submit twice the same reveal op
            assert level not in reveal_ops
            # level should match a seed
            assert level in seed_nonce_hashes
            reveal_ops[level] = content['nonce']

        # check all nonce hashes have been revealed
        assert len(reveal_ops) == len(seed_nonce_hashes)
        # we could go a step further and check that revelations are correct
back to top