示例#1
0
def test_headerdb_persist_disconnected_headers(headerdb, genesis_header):
    headerdb.persist_header(genesis_header)

    headers = mk_header_chain(genesis_header, length=10)

    score_at_pseudo_genesis = 154618822656
    # This is the score that we would reach at the tip if we persist the entire chain.
    # But we test to reach it by building on top of a trusted score.
    expected_score_at_tip = 188978561024

    pseudo_genesis = headers[7]

    # Persist the checkpoint header with a trusted score
    headerdb.persist_checkpoint_header(pseudo_genesis, score_at_pseudo_genesis)

    assert_headers_eq(headerdb.get_canonical_head(), pseudo_genesis)

    headers_from_pseudo_genesis = (
        headers[8],
        headers[9],
    )

    headerdb.persist_header_chain(headers_from_pseudo_genesis,
                                  pseudo_genesis.parent_hash)
    head = headerdb.get_canonical_head()
    assert_headers_eq(head, headers[-1])
    assert headerdb.get_score(head.hash) == expected_score_at_tip
示例#2
0
def test_headerdb_persist_header_returns_new_canonical_chain(headerdb, genesis_header):
    gen_result, _ = headerdb.persist_header(genesis_header)
    assert gen_result == (genesis_header,)

    chain_a = mk_header_chain(genesis_header, 3)
    chain_b = mk_header_chain(genesis_header, 2)
    chain_c = mk_header_chain(genesis_header, 5)

    for header in chain_a:
        res, _ = headerdb.persist_header(header)
        assert res == (header,)

    for header in chain_b:
        res, _ = headerdb.persist_header(header)
        assert res == tuple()

    for idx, header in enumerate(chain_c, 1):
        res, _ = headerdb.persist_header(header)
        if idx <= 3:
            # prior to passing up `chain_a` each import should not return new
            # canonical headers.
            assert res == tuple()
        elif idx == 4:
            # at the point where `chain_c` passes `chain_a` we should get the
            # headers from `chain_c` up through current.
            assert res == chain_c[:idx]
            assert_headers_eq(res[-1], header)
        else:
            # after `chain_c` has become canonical we should just get each new
            # header back.
            assert res == (header,)
示例#3
0
def test_different_cases_of_patching_gaps(headerdb, genesis_header, steps):
    headerdb.persist_header(genesis_header)
    new_chain_length = 10
    chain_a = mk_header_chain(genesis_header, length=new_chain_length)
    chain_b = mk_header_chain(genesis_header, length=new_chain_length)

    def _get_chain(id):
        if chain_id == 'a':
            return chain_a
        elif chain_id == 'b':
            return chain_b
        else:
            raise Exception(f"Invalid chain id: {chain_id}")

    assert headerdb.get_header_chain_gaps() == GENESIS_CHAIN_GAPS

    for step_index, step in enumerate(
            steps):  # noqa: B007  # step_index only present for debugging
        step_action, step_data = step
        if step_action is StepAction.PERSIST_CHECKPOINT:
            pseudo_genesis = chain_a[step_data - 1]
            pseudo_genesis_score = get_score(genesis_header,
                                             chain_a[0:step_data])
            headerdb.persist_checkpoint_header(pseudo_genesis,
                                               pseudo_genesis_score)
        elif step_action is StepAction.PERSIST_HEADERS:
            chain_id, selector_fn = step_data
            headers = selector_fn(_get_chain(chain_id))
            headerdb.persist_header_chain(headers)
        elif step_action is StepAction.VERIFY_GAPS:
            gaps = headerdb.get_header_chain_gaps()
            assert gaps == step_data
            all_gap_numbers = _all_gap_numbers(
                gaps, highest_block_number=new_chain_length + 1)
            for missing_block_number in all_gap_numbers:
                with pytest.raises(HeaderNotFound):
                    headerdb.get_canonical_block_header_by_number(
                        missing_block_number)
        elif step_action is StepAction.VERIFY_PERSIST_RAISES:
            chain_id, error, selector_fn = step_data
            headers = selector_fn(_get_chain(chain_id))
            with pytest.raises(error):
                headerdb.persist_header_chain(headers)
        elif step_action is StepAction.VERIFY_CANONICAL_HEAD:
            # save actual and expected for easy reading on a failed test
            actual_canonical = headerdb.get_canonical_head()
            expected_canonical = chain_a[step_data - 1]
            assert_headers_eq(actual_canonical, expected_canonical)
        elif step_action is StepAction.VERIFY_CANONICAL_HEADERS:
            chain_id, selector_fn = step_data
            for header in selector_fn(_get_chain(chain_id)):
                assert headerdb.get_canonical_block_header_by_number(
                    header.block_number) == header
        else:
            raise Exception("Unknown step action")

        _validate_gap_invariants(headerdb.get_header_chain_gaps())
        _validate_consecutive_canonical_links(headerdb, new_chain_length + 1)
示例#4
0
def test_headerdb_get_canonical_head_with_header_chain(headerdb, genesis_header):
    headerdb.persist_header(genesis_header)

    headers = mk_header_chain(genesis_header, length=10)

    headerdb.persist_header_chain(headers)

    head = headerdb.get_canonical_head()
    assert_headers_eq(head, headers[-1])
示例#5
0
def test_headerdb_get_canonical_head_with_header_chain(headerdb, genesis_header):
    headerdb.persist_header(genesis_header)

    headers = mk_header_chain(genesis_header, length=10)

    headerdb.persist_header_chain(headers)

    head = headerdb.get_canonical_head()
    assert headerdb.get_score(head.hash) == 188978561024
    assert_headers_eq(head, headers[-1])
示例#6
0
def test_header_chain_initialization_header_already_persisted(
        base_db, genesis_header):
    headerdb = HeaderDB(base_db)
    headerdb.persist_header(genesis_header)

    # sanity check that the header is persisted
    assert_headers_eq(headerdb.get_canonical_head(), genesis_header)

    header_chain = HeaderChain.from_genesis_header(base_db, genesis_header)

    head = header_chain.get_canonical_head()
    assert_headers_eq(head, genesis_header)
示例#7
0
def test_headerdb_canonical_header_retrieval_by_number(headerdb, genesis_header):
    headerdb.persist_header(genesis_header)

    headers = mk_header_chain(genesis_header, length=10)

    headerdb.persist_header_chain(headers)

    # can we get the genesis header by hash
    actual = headerdb.get_canonical_block_header_by_number(genesis_header.block_number)
    assert_headers_eq(actual, genesis_header)

    for header in headers:
        actual = headerdb.get_canonical_block_header_by_number(header.block_number)
        assert_headers_eq(actual, header)
示例#8
0
def test_headerdb_get_canonical_head_with_header_chain_iterator(
        headerdb, genesis_header, chain_length):

    headerdb.persist_header(genesis_header)
    headers = mk_header_chain(genesis_header, length=chain_length)

    headerdb.persist_header_chain(header for header in headers)

    head = headerdb.get_canonical_head()

    if chain_length == 0:
        assert_headers_eq(head, genesis_header)
    else:
        assert_headers_eq(head, headers[-1])
示例#9
0
def test_headerdb_persist_disconnected_headers(headerdb, genesis_header):
    headerdb.persist_header(genesis_header)

    headers = mk_header_chain(genesis_header, length=10)

    # This is the score that we would reach at the tip if we persist the entire chain.
    # But we test to reach it by building on top of a trusted score.
    expected_score_at_tip = get_score(genesis_header, headers)

    pseudo_genesis = headers[7]
    pseudo_genesis_score = get_score(genesis_header, headers[0:8])

    assert headerdb.get_header_chain_gaps() == GENESIS_CHAIN_GAPS
    # Persist the checkpoint header with a trusted score
    headerdb.persist_checkpoint_header(pseudo_genesis, pseudo_genesis_score)
    assert headerdb.get_header_chain_gaps() == (((1, 7),), 9)

    assert_headers_eq(headerdb.get_canonical_head(), pseudo_genesis)

    headers_from_pseudo_genesis = (headers[8], headers[9],)

    headerdb.persist_header_chain(headers_from_pseudo_genesis, pseudo_genesis.parent_hash)
    assert headerdb.get_header_chain_gaps() == (((1, 7),), 11)
    head = headerdb.get_canonical_head()
    assert_headers_eq(head, headers[-1])
    assert headerdb.get_score(head.hash) == expected_score_at_tip

    header_8 = headerdb.get_block_header_by_hash(headers[8].hash)
    assert_headers_eq(header_8, headers[8])

    with pytest.raises(HeaderNotFound):
        headerdb.get_block_header_by_hash(headers[2].hash)
示例#10
0
def assert_is_canonical_chain(headerdb, headers):
    if not headers:
        return

    # verify that the HEAD is correctly set.
    head = headerdb.get_canonical_head()
    assert_headers_eq(head, headers[-1])

    # verify that each header is set as the canonical block.
    for header in headers:
        canonical_hash = headerdb.get_canonical_block_hash(header.block_number)
        assert canonical_hash == header.hash

    # verify difficulties are correctly set.
    base_header = headerdb.get_block_header_by_hash(headers[0].parent_hash)

    difficulties = tuple(h.difficulty for h in headers)
    scores = tuple(accumulate(operator.add, difficulties, base_header.difficulty))

    for header, expected_score in zip(headers, scores[1:]):
        actual_score = headerdb.get_score(header.hash)
        assert actual_score == expected_score
示例#11
0
def test_chaindb_get_block_header_by_hash(chaindb, block, header):
    block = block.copy(header=set_empty_root(chaindb, block.header))
    header = set_empty_root(chaindb, header)
    chaindb.persist_block(block)
    block_header = chaindb.get_block_header_by_hash(block.hash)
    assert_headers_eq(block_header, header)
示例#12
0
def test_header_chain_initialization_from_genesis_header(
        base_db, genesis_header):
    header_chain = HeaderChain.from_genesis_header(base_db, genesis_header)

    head = header_chain.get_canonical_head()
    assert_headers_eq(head, genesis_header)
示例#13
0
def test_blockchain_fixtures(fixture_data, fixture):
    try:
        chain = new_chain_from_fixture(fixture)
    except ValueError as e:
        raise AssertionError(f"could not load chain for {fixture_data}") from e

    genesis_fields = genesis_fields_from_fixture(fixture)

    genesis_block = chain.get_canonical_block_by_number(0)
    genesis_header = genesis_block.header

    # Validate the genesis header RLP against the generated header
    if 'genesisRLP' in fixture:
        # Super hacky, but better than nothing: extract the header, then re-decode it
        fixture_decoded_block = rlp.decode(fixture['genesisRLP'])
        fixture_encoded_header = rlp.encode(fixture_decoded_block[0])
        fixture_header = rlp.decode(fixture_encoded_header, sedes=HeaderSedes)
        # Error message with pretty output if header doesn't match
        assert_headers_eq(fixture_header, genesis_header)
        # Last gut check that transactions & receipts are valid, too
        assert rlp.encode(genesis_block) == fixture['genesisRLP']

    assert_imported_genesis_header_unchanged(genesis_fields, genesis_header)

    # 1 - mine the genesis block
    # 2 - loop over blocks:
    #     - apply transactions
    #     - mine block
    # 3 - diff resulting state with expected state
    # 4 - check that all previous blocks were valid

    for block_fixture in fixture['blocks']:
        should_be_good_block = 'expectException' not in block_fixture

        if 'rlp_error' in block_fixture:
            assert not should_be_good_block
            continue

        if should_be_good_block:
            (original_block, executed_block,
             block_rlp) = apply_fixture_block_to_chain(
                 block_fixture,
                 chain,
                 perform_validation=False  # we manually validate below
             )
            assert_mined_block_unchanged(original_block, executed_block)
            chain.validate_block(original_block)
        else:
            try:
                apply_fixture_block_to_chain(block_fixture, chain)
            except EXPECTED_BAD_BLOCK_EXCEPTIONS:
                # failure is expected on this bad block
                pass
            else:
                raise AssertionError(
                    "Block should have caused a validation error")

    latest_block_hash = chain.get_canonical_block_by_number(
        chain.get_block().number - 1).hash
    if latest_block_hash != fixture['lastblockhash']:
        verify_state(fixture['postState'], chain.get_vm().state)