示例#1
0
def test_farm_block_one_spendbundle():
    REWARD = 10000
    unspent_db = RAM_DB()
    chain_view = ChainView.for_genesis_hash(GENESIS_BLOCK, unspent_db)

    pos = ProofOfSpace(get_pool_public_key(), get_plot_public_key())

    puzzle_hash = puzzle_hash_for_index(1)

    empty_spend_bundle = SpendBundle.aggregate([])
    header, header_signature, body = farm_block(GENESIS_BLOCK,
                                                Signature.zero(), 1, pos,
                                                empty_spend_bundle,
                                                puzzle_hash, REWARD)
    coinbase_coin = body.coinbase_coin

    conditions = standard_conditions()
    spend_bundle = spend_coin(coin=coinbase_coin,
                              conditions=conditions,
                              index=1)

    header, header_signature, body = farm_block(GENESIS_BLOCK,
                                                Signature.zero(), 1, pos,
                                                spend_bundle, puzzle_hash,
                                                REWARD)
    removals = removals_for_body(body)
    assert len(removals) == 1
    assert removals[0] == list(spend_bundle.coin_solutions)[0].coin.name()

    run = asyncio.get_event_loop().run_until_complete
    additions, removals = run(
        chain_view.accept_new_block(header, unspent_db, REWARD, 0))
    assert len(additions) == 4
    assert len(removals) == 1
示例#2
0
def test_farm_two_blocks():
    """
    In this test, we farm two blocks: one empty block,
    then one block which spends the coinbase transaction from the empty block.
    """
    REWARD = 10000
    unspent_db = RAM_DB()
    chain_view = ChainView.for_genesis_hash(GENESIS_BLOCK, unspent_db)

    assert chain_view.genesis_hash == GENESIS_BLOCK
    assert chain_view.tip_hash == HeaderHash(GENESIS_BLOCK)
    assert chain_view.tip_index == 0
    assert chain_view.unspent_db == unspent_db

    pos_1 = ProofOfSpace(get_pool_public_key(), get_plot_public_key())

    puzzle_hash = puzzle_hash_for_index(1)

    empty_spend_bundle = SpendBundle.aggregate([])
    header, header_signature, body = farm_block(GENESIS_BLOCK,
                                                Signature.zero(), 1, pos_1,
                                                empty_spend_bundle,
                                                puzzle_hash, REWARD)

    run = asyncio.get_event_loop().run_until_complete
    additions, removals = run(
        chain_view.accept_new_block(header, unspent_db, REWARD, 0))
    assert len(additions) == 2
    assert len(removals) == 0
    # TODO: check additions
    assert additions[1].puzzle_hash == body.fees_coin.puzzle_hash
    assert additions[1].amount == 0

    chain_view = run(
        chain_view.augment_chain_view(header, header_signature, unspent_db,
                                      unspent_db, REWARD, 0))

    assert chain_view.genesis_hash == GENESIS_BLOCK
    assert chain_view.tip_hash == HeaderHash(header)
    assert chain_view.tip_index == 1
    assert chain_view.unspent_db == unspent_db

    conditions = standard_conditions()
    spend_bundle_2 = spend_coin(coin=additions[0],
                                conditions=conditions,
                                index=1)

    assert validate_spend_bundle_signature(spend_bundle_2)

    pos_2 = ProofOfSpace(get_pool_public_key(1), get_plot_public_key())

    header_2, header_signature_2, body_2 = farm_block(header, header_signature,
                                                      2, pos_2, spend_bundle_2,
                                                      puzzle_hash, REWARD)
    print(header_2)
    print(header_signature_2)

    removals = removals_for_body(body_2)
    assert len(removals) == 1
    assert removals[0] == list(spend_bundle_2.coin_solutions)[0].coin.name()
示例#3
0
def make_client_server():
    init_logging()
    run = asyncio.get_event_loop().run_until_complete
    path = pathlib.Path(tempfile.mkdtemp(), "port")
    server, aiter = run(start_unix_server_aiter(path))
    rws_aiter = map_aiter(lambda rw: dict(
        reader=rw[0], writer=rw[1], server=server), aiter)
    initial_block_hash = bytes(([0] * 31) + [1])
    ledger = ledger_api.LedgerAPI(initial_block_hash, RAM_DB())
    server_task = asyncio.ensure_future(api_server(rws_aiter, ledger))
    remote = run(proxy_for_unix_connection(path))
    # make sure server_task isn't garbage collected
    remote.server_task = server_task
    return remote
示例#4
0
def test_client_server():
    init_logging()

    run = asyncio.get_event_loop().run_until_complete

    path = pathlib.Path(tempfile.mkdtemp(), "port")

    server, aiter = run(start_unix_server_aiter(path))

    rws_aiter = map_aiter(
        lambda rw: dict(reader=rw[0], writer=rw[1], server=server), aiter)

    initial_block_hash = bytes(([0] * 31) + [1])
    ledger = ledger_api.LedgerAPI(initial_block_hash, RAM_DB())
    server_task = asyncio.ensure_future(api_server(rws_aiter, ledger))

    run(client_test(path))
    server_task.cancel()
示例#5
0
def test_farm_block_empty():
    REWARD = 10000
    unspent_db = RAM_DB()
    chain_view = ChainView.for_genesis_hash(GENESIS_BLOCK, unspent_db)

    pos = ProofOfSpace(get_pool_public_key(), get_plot_public_key())

    puzzle_hash = puzzle_hash_for_index(1)

    spend_bundle = SpendBundle.aggregate([])

    header, header_signature, body = farm_block(GENESIS_BLOCK,
                                                Signature.zero(), 1, pos,
                                                spend_bundle, puzzle_hash,
                                                REWARD)
    removals = removals_for_body(body)
    assert len(removals) == 0

    run = asyncio.get_event_loop().run_until_complete
    additions, removals = run(
        chain_view.accept_new_block(header, unspent_db, REWARD, 0))
    assert len(additions) == 2
    assert len(removals) == 0
示例#6
0
def run_ledger_api(server, aiter):
    db = RAM_DB()
    INITIAL_BLOCK_HASH = bytes(([0] * 31) + [1])
    ledger = ledger_api.LedgerAPI(INITIAL_BLOCK_HASH, db)
    rws_aiter = map_aiter(lambda rw: dict(reader=rw[0], writer=rw[1], server=server), aiter)
    return api_server(rws_aiter, ledger)
示例#7
0
async def accept_new_block(chain_view: ChainView, header: Header,
                           storage: Storage, coinbase_reward: int,
                           timestamp: int):
    """
    Checks the block against the existing ChainView object.
    Returns a list of additions (coins), and removals (coin names).

    Missing blobs must be resolvable by storage.

    If the given block is invalid, a ConsensusError is raised.
    """

    try:
        newly_created_block_index = chain_view.tip_index + 1

        # verify header extends current view

        if header.previous_hash != chain_view.tip_hash:
            raise ConsensusError(Err.DOES_NOT_EXTEND, header)

        # get body

        body = await header.body_hash.obj(storage)
        if body is None:
            raise ConsensusError(Err.MISSING_FROM_STORAGE, header.body_hash)

        # ensure block program generates solutions

        npc_list = name_puzzle_conditions_list(body.solution_program)

        # build removals list
        removals = tuple(_[0] for _ in npc_list)

        # build additions

        def additions_iter(body, npc_list):
            yield body.coinbase_coin
            yield body.fees_coin
            for coin_name, puzzle_hash, conditions_dict in npc_list:
                for _ in created_outputs_for_conditions_dict(
                        conditions_dict, coin_name):
                    yield _

        additions = tuple(additions_iter(body, npc_list))
        for coin in additions:
            if coin.amount >= MAX_COIN_AMOUNT:
                raise ConsensusError(Err.COIN_AMOUNT_EXCEEDS_MAXIMUM, coin)

        #  watch out for duplicate outputs

        addition_counter = collections.Counter(_.name() for _ in additions)
        for k, v in addition_counter.items():
            if v > 1:
                raise ConsensusError(Err.DUPLICATE_OUTPUT, k)

        #  watch out for double-spends

        removal_counter = collections.Counter(removals)
        for k, v in removal_counter.items():
            if v > 1:
                raise ConsensusError(Err.DOUBLE_SPEND, k)

        # create a temporary overlay DB with the additions

        ram_storage = RAM_DB()
        for _ in additions:
            await ram_storage.add_preimage(bytes(_))

        ram_db = RAMUnspentDB(additions, newly_created_block_index)
        overlay_storage = OverlayStorage(ram_storage, storage)
        unspent_db = OverlayUnspentDB(chain_view.unspent_db, ram_db)

        coin_futures = [
            asyncio.ensure_future(_[0].obj(overlay_storage)) for _ in npc_list
        ]

        # build cpc_list from npc_list
        cpc_list = []
        for coin_future, (coin_name, puzzle_hash,
                          conditions_dict) in zip(coin_futures, npc_list):
            coin = await coin_future
            if coin is None:
                raise ConsensusError(Err.UNKNOWN_UNSPENT, coin_name)
            cpc_list.append((coin, puzzle_hash, conditions_dict))

        # check that the revealed removal puzzles actually match the puzzle hash

        for coin, puzzle_hash, conditions_dict in cpc_list:
            if puzzle_hash != coin.puzzle_hash:
                raise ConsensusError(Err.WRONG_PUZZLE_HASH, coin)

        # build coin_to_unspent dictionary

        futures = []
        for coin, puzzle_hash, conditions_dict in cpc_list:
            futures.append(
                asyncio.ensure_future(
                    unspent_db.unspent_for_coin_name(coin.name())))

        coin_to_unspent = {}
        for (coin, puzzle_hash,
             conditions_dict), future in zip(cpc_list, futures):
            coin_to_unspent[coin.name()] = await future

        # check removals against UnspentDB

        for coin, puzzle_hash, conditions_dict in cpc_list:
            if coin in additions:
                # it's an ephemeral coin, created and destroyed in the same block
                continue
            coin_name = coin.name()
            unspent = coin_to_unspent[coin_name]
            if (unspent is None or unspent.confirmed_block_index == 0
                    or unspent.confirmed_block_index > chain_view.tip_index):
                raise ConsensusError(Err.UNKNOWN_UNSPENT, coin_name)
            if (0 < unspent.spent_block_index <= chain_view.tip_index):
                raise ConsensusError(Err.DOUBLE_SPEND, coin_name)

        # check fees

        fees = 0
        for coin, puzzle_hash, conditions_dict in cpc_list:
            fees -= coin.amount

        for coin in additions:
            fees += coin.amount

        if fees != coinbase_reward:
            raise ConsensusError(Err.BAD_COINBASE_REWARD, body.coinbase_coin)

        # check solution for each CoinSolution pair
        # this is where CHECKLOCKTIME etc. are verified

        context = dict(
            block_index=newly_created_block_index,
            removals=set(removals),
            coin_to_unspent=coin_to_unspent,
            creation_time=timestamp,
        )
        hash_key_pairs = []
        for coin, puzzle_hash, conditions_dict in cpc_list:
            check_conditions_dict(coin, conditions_dict, context)
            hash_key_pairs.extend(
                hash_key_pairs_for_conditions_dict(conditions_dict))

        # verify aggregated signature

        if not body.aggregated_signature.validate(hash_key_pairs):
            raise ConsensusError(Err.BAD_AGGREGATE_SIGNATURE, body)

        return additions, removals

    except ConsensusError:
        raise
    except Exception as ex:
        breakpoint()
        raise ConsensusError(Err.UNKNOWN, ex)