Exemple #1
0
// en.bitcoin.it/wiki/Protocol_documentation#getdata
// getdata can be used to retrieve transactions, but only if they are
// in the memory pool or relay set - arbitrary access to transactions
// in the  chain is not allowed to avoid having clients start to depend
// on nodes having full transaction indexes (which modern nodes do not).
void responder::send_chain_tx(const code& ec, const transaction& tx,
    const hash_digest& tx_hash, channel::ptr node)
{
    if (ec == error::service_stopped)
        return;

    if (ec == error::not_found)
    {
        log::debug(LOG_RESPONDER)
            << "Transaction for [" << node->authority()
            << "] not in blockchain [" << encode_hash(tx_hash) << "]";

        // It wasn't in the blockchain, so send notfound.
        send_tx_not_found(tx_hash, node);
        return;
    }

    if (ec)
    {
        log::error(LOG_RESPONDER)
            << "Failure fetching blockchain tx data for ["
            << node->authority() << "] " << ec.message();
        node->stop(ec);
        return;
    }

    send_tx(tx, tx_hash, node);
}
Exemple #2
0
// We don't seem to be getting getdata requests.
void responder::receive_get_data(const code& ec, const get_data& packet,
    channel::ptr node)
{
    if (ec == error::channel_stopped)
        return;

    const auto peer = node->authority();

    if (ec)
    {
        log::debug(LOG_RESPONDER)
            << "Failure in receive get data [" << peer << "] " << ec.message();
        node->stop(ec);
        return;
    }

    // Resubscribe to serve tx and blocks.
    node->subscribe<message::get_data>(
        std::bind(&responder::receive_get_data,
            this, _1, _2, node));

    log::debug(LOG_RESPONDER)
        << "Getdata BEGIN [" << peer << "] "
        << "txs (" << packet.count(inventory_type_id::transaction) << ") "
        << "blocks (" << packet.count(inventory_type_id::block) << ") "
        << "bloom (" << packet.count(inventory_type_id::filtered_block) << ")";

    for (const auto& inventory: packet.inventories)
    {
        switch (inventory.type)
        {
            case inventory_type_id::transaction:
                log::debug(LOG_RESPONDER)
                    << "Transaction getdata for [" << peer << "] "
                    << encode_hash(inventory.hash);
                tx_pool_.fetch(inventory.hash,
                    std::bind(&responder::send_pool_tx,
                        this, _1, _2, inventory.hash, node));
                break;

            case inventory_type_id::block:
                log::debug(LOG_RESPONDER)
                    << "Block getdata for [" << peer << "] "
                    << encode_hash(inventory.hash);
                block_fetcher::fetch(blockchain_, inventory.hash,
                    std::bind(&responder::send_block,
                        this, _1, _2, inventory.hash, node));
                break;

            case inventory_type_id::error:
            case inventory_type_id::none:
            default:
                log::debug(LOG_RESPONDER)
                    << "Ignoring invalid getdata type for [" << peer << "]";
        }
    }

    log::debug(LOG_RESPONDER)
        << "Getdata END [" << peer << "]";
}
Exemple #3
0
void responder::send_pool_tx(const code& ec, const transaction& tx,
    const hash_digest& tx_hash, channel::ptr node)
{
    if (ec == error::service_stopped)
        return;

    if (ec == error::not_found)
    {
        log::debug(LOG_RESPONDER)
            << "Transaction for [" << node->authority()
            << "] not in mempool [" << encode_hash(tx_hash) << "]";

        // It wasn't in the mempool, so relay the request to the blockchain.
        blockchain_.fetch_transaction(tx_hash,
            std::bind(&responder::send_chain_tx,
                this, _1, _2, tx_hash, node));
        return;
    }

    if (ec)
    {
        log::error(LOG_RESPONDER)
            << "Failure fetching mempool tx data for ["
            << node->authority() << "] " << ec.message();
        node->stop(ec);
        return;
    }

    send_tx(tx, tx_hash, node);
}
void session::handle_start(const code& ec, channel::ptr channel,
    result_handler handle_started, result_handler handle_stopped)
{
    // Must either stop or subscribe the channel for stop before returning.
    if (ec)
        channel->stop(ec);
    else
        channel->subscribe_stop(handle_stopped);

    // This is the end of the registration sequence.
    handle_started(ec);
}
Exemple #5
0
void protocol::handle_handshake(const code& ec, channel::ptr node)
{
    if (ec)
    {
        log_debug(LOG_PROTOCOL) << "Failure in peer handshake ["
            << node->address() << "] " << ec.message();
        node->stop(ec);
        return;
    }

    // Attach ping protocol to the new connection (until node stop event).
    std::make_shared<protocol_ping>(node, pool_, timeouts_.heartbeat)->start();

    // Attach address protocol to the new connection (until node stop event).
    std::make_shared<protocol_address>(node, pool_, hosts_, self_)->start();
}
void session::handle_start(const code& ec, channel::ptr channel,
    result_handler handle_started, result_handler handle_stopped)
{
    // Must either stop or subscribe the channel for stop before returning.
    // All closures must eventually be invoked as otherwise it is a leak.
    // Therefore upon start failure expect start failure and stop callbacks.
    if (ec)
    {
        channel->stop(ec);
        handle_stopped(ec);
    }
    else
    {
        channel->subscribe_stop(
            BIND_3(do_remove, _1, channel, handle_stopped));
    }

    // This is the end of the registration sequence.
    handle_started(ec);
}
Exemple #7
0
// Should we look in the orphan pool first?
void responder::send_block(const code& ec, const block& block,
    const hash_digest& block_hash, channel::ptr node)
{
    if (ec == error::service_stopped)
        return;

    if (ec == error::not_found)
    {
        log::debug(LOG_RESPONDER)
            << "Block for [" << node->authority()
            << "] not in blockchain [" << encode_hash(block_hash) << "]";

        // It wasn't in the blockchain, so send notfound.
        send_block_not_found(block_hash, node);
    }

    if (ec)
    {
        log::error(LOG_RESPONDER)
            << "Failure fetching block data for ["
            << node->authority() << "] " << ec.message();
        node->stop(ec);
        return;
    }

    const auto send_handler = [block_hash, node](const code& ec)
    {
        if (ec)
            log::debug(LOG_RESPONDER)
                << "Failure sending block for ["
                << node->authority() << "]";
        else
            log::debug(LOG_RESPONDER)
                << "Sent block for [" << node->authority()
                << "] " << encode_hash(block_hash);
    };

    node->send(block, send_handler);
}
void session_batch::converge(const code& ec, channel::ptr channel,
     atomic_counter_ptr counter, upgrade_mutex_ptr mutex,
     channel_handler handler)
{
    ///////////////////////////////////////////////////////////////////////////
    // Critical Section
    mutex->lock_upgrade();

    const auto initial_count = counter->load();
    BITCOIN_ASSERT(initial_count <= batch_size_);

    // Already completed, don't call handler.
    if (initial_count == batch_size_)
    {
        mutex->unlock_upgrade();
        //-----------------------------------------------------------------
        if (!ec)
            channel->stop(error::channel_stopped);

        return;
    }

    const auto count = !ec ? batch_size_ : initial_count + 1;
    const auto cleared = count == batch_size_;

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    mutex->unlock_upgrade_and_lock();
    counter->store(count);
    mutex->unlock();
    ///////////////////////////////////////////////////////////////////////

    if (cleared)
    {
        // If the last connection attempt is an error, normalize the code.
        const auto result = ec ? error::operation_failed : error::success;
        handler(result, channel);
    }
}