mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-12 18:44:59 +01:00
205 lines
9.3 KiB
C++
205 lines
9.3 KiB
C++
// Copyright (c) 2024 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <capnp/blob.h>
|
|
#include <capnp/capability.h>
|
|
#include <capnp/list.h>
|
|
#include <coins.h>
|
|
#include <interfaces/chain.h>
|
|
#include <interfaces/handler.h>
|
|
#include <interfaces/ipc.h>
|
|
#include <ipc/capnp/chain-types.h>
|
|
#include <ipc/capnp/chain.capnp.h>
|
|
#include <ipc/capnp/chain.capnp.proxy.h>
|
|
#include <ipc/capnp/chain.capnp.proxy-types.h>
|
|
#include <ipc/capnp/common-types.h>
|
|
#include <ipc/capnp/context.h>
|
|
#include <ipc/capnp/handler.capnp.proxy.h>
|
|
#include <mp/proxy-io.h>
|
|
#include <mp/proxy-types.h>
|
|
#include <mp/util.h>
|
|
#include <primitives/block.h>
|
|
#include <rpc/server.h>
|
|
#include <streams.h>
|
|
#include <uint256.h>
|
|
#include <undo.h>
|
|
|
|
#include <assert.h>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace mp {
|
|
void CustomBuildMessage(InvokeContext& invoke_context,
|
|
const interfaces::FoundBlock& dest,
|
|
ipc::capnp::messages::FoundBlockParam::Builder&& builder)
|
|
{
|
|
if (dest.m_hash) builder.setWantHash(true);
|
|
if (dest.m_height) builder.setWantHeight(true);
|
|
if (dest.m_time) builder.setWantTime(true);
|
|
if (dest.m_max_time) builder.setWantMaxTime(true);
|
|
if (dest.m_mtp_time) builder.setWantMtpTime(true);
|
|
if (dest.m_in_active_chain) builder.setWantInActiveChain(true);
|
|
if (dest.m_locator) builder.setWantLocator(true);
|
|
if (dest.m_next_block) CustomBuildMessage(invoke_context, *dest.m_next_block, builder.initNextBlock());
|
|
if (dest.m_data) builder.setWantData(true);
|
|
}
|
|
|
|
void FindBlock(const std::function<void()>& find,
|
|
const ipc::capnp::messages::FoundBlockParam::Reader& reader,
|
|
ipc::capnp::messages::FoundBlockResult::Builder&& builder,
|
|
interfaces::FoundBlock& found_block)
|
|
{
|
|
uint256 hash;
|
|
int height = -1;
|
|
int64_t time = -1;
|
|
int64_t max_time = -1;
|
|
int64_t mtp_time = -1;
|
|
bool in_active_chain = -1;
|
|
CBlockLocator locator;
|
|
CBlock data;
|
|
if (reader.getWantHash()) found_block.hash(hash);
|
|
if (reader.getWantHeight()) found_block.height(height);
|
|
if (reader.getWantTime()) found_block.time(time);
|
|
if (reader.getWantMaxTime()) found_block.maxTime(max_time);
|
|
if (reader.getWantMtpTime()) found_block.mtpTime(mtp_time);
|
|
if (reader.getWantInActiveChain()) found_block.inActiveChain(in_active_chain);
|
|
if (reader.getWantLocator()) found_block.locator(locator);
|
|
if (reader.getWantData()) found_block.data(data);
|
|
if (reader.hasNextBlock()) {
|
|
interfaces::FoundBlock next_block;
|
|
found_block.nextBlock(next_block);
|
|
FindBlock(find, reader.getNextBlock(), builder.initNextBlock(), next_block);
|
|
} else {
|
|
find();
|
|
}
|
|
if (!found_block.found) return;
|
|
if (reader.getWantHash()) builder.setHash(ipc::capnp::ToArray(ipc::capnp::Serialize(hash)));
|
|
if (reader.getWantHeight()) builder.setHeight(height);
|
|
if (reader.getWantTime()) builder.setTime(time);
|
|
if (reader.getWantMaxTime()) builder.setMaxTime(max_time);
|
|
if (reader.getWantMtpTime()) builder.setMtpTime(mtp_time);
|
|
if (reader.getWantInActiveChain()) builder.setInActiveChain(in_active_chain);
|
|
if (reader.getWantLocator()) builder.setLocator(ipc::capnp::ToArray(ipc::capnp::Serialize(locator)));
|
|
if (reader.getWantData()) builder.setData(ipc::capnp::ToArray(ipc::capnp::Serialize(data)));
|
|
builder.setFound(true);
|
|
}
|
|
|
|
void CustomPassMessage(InvokeContext& invoke_context,
|
|
const ipc::capnp::messages::FoundBlockParam::Reader& reader,
|
|
ipc::capnp::messages::FoundBlockResult::Builder&& builder,
|
|
std::function<void(const interfaces::FoundBlock&)>&& fn)
|
|
{
|
|
interfaces::FoundBlock found_block;
|
|
FindBlock([&] { fn(found_block); }, reader, std::move(builder), found_block);
|
|
}
|
|
|
|
void CustomReadMessage(InvokeContext& invoke_context,
|
|
const ipc::capnp::messages::FoundBlockResult::Reader& reader,
|
|
const interfaces::FoundBlock& dest)
|
|
{
|
|
if (!reader.getFound()) return;
|
|
if (dest.m_hash) *dest.m_hash = ipc::capnp::Unserialize<uint256>(reader.getHash());
|
|
if (dest.m_height) *dest.m_height = reader.getHeight();
|
|
if (dest.m_time) *dest.m_time = reader.getTime();
|
|
if (dest.m_max_time) *dest.m_max_time = reader.getMaxTime();
|
|
if (dest.m_mtp_time) *dest.m_mtp_time = reader.getMtpTime();
|
|
if (dest.m_in_active_chain) *dest.m_in_active_chain = reader.getInActiveChain();
|
|
if (dest.m_locator) *dest.m_locator = ipc::capnp::Unserialize<CBlockLocator>(reader.getLocator());
|
|
if (dest.m_next_block) CustomReadMessage(invoke_context, reader.getNextBlock(), *dest.m_next_block);
|
|
if (dest.m_data) *dest.m_data = ipc::capnp::Unserialize<CBlock>(reader.getData());
|
|
dest.found = true;
|
|
}
|
|
|
|
void CustomBuildMessage(InvokeContext& invoke_context,
|
|
const interfaces::BlockInfo& block,
|
|
ipc::capnp::messages::BlockInfo::Builder&& builder)
|
|
{
|
|
// Pointer fields are annotated with Proxy.skip so need to be filled
|
|
// manually. Generated code would actually work correctly, though, so this
|
|
// could be dropped if there were a Proxy.skip(read_only=True) half-skip
|
|
// option.
|
|
builder.setHash(ipc::capnp::ToArray(block.hash));
|
|
if (block.prev_hash) builder.setPrevHash(ipc::capnp::ToArray(*block.prev_hash));
|
|
if (block.data) builder.setData(ipc::capnp::ToArray(ipc::capnp::Serialize(*block.data)));
|
|
if (block.undo_data) builder.setUndoData(ipc::capnp::ToArray(ipc::capnp::Serialize(*block.undo_data)));
|
|
// Copy the remaining fields using the code generated by Proxy.wrap.
|
|
mp::BuildOne<0>(mp::TypeList<interfaces::BlockInfo>(), invoke_context, builder, block);
|
|
}
|
|
|
|
void CustomPassMessage(InvokeContext& invoke_context,
|
|
const ipc::capnp::messages::BlockInfo::Reader& reader,
|
|
::capnp::Void builder,
|
|
std::function<void(const interfaces::BlockInfo&)>&& fn)
|
|
{
|
|
// Pointer fields are annotated with Proxy.skip because code generator can't
|
|
// figure out pointer lifetimes to be able to implementat a ReadField
|
|
// implementation for the BlockInfo struct, and the default PassField
|
|
// function calls readfield. In theory though, code generator could create a
|
|
// PassField specialization for the struct which could allocate pointers for
|
|
// the lifetime of the call, like the code below is doing manually. This
|
|
// would take care of all pointer fields, though the BlockInfo.hash
|
|
// reference field would still need to be handled specially. Or even
|
|
// BlockInfo.hash field could be handled automatically if the generated code
|
|
// used C++20 designated member initialization.
|
|
const uint256 hash = ipc::capnp::ToBlob<uint256>(reader.getHash());
|
|
std::optional<const uint256> prev_hash;
|
|
std::optional<const CBlock> data;
|
|
std::optional<const CBlockUndo> undo_data;
|
|
interfaces::BlockInfo block{hash};
|
|
if (reader.hasPrevHash()) {
|
|
prev_hash.emplace(ipc::capnp::ToBlob<uint256>(reader.getPrevHash()));
|
|
block.prev_hash = &*prev_hash;
|
|
}
|
|
if (reader.hasData()) {
|
|
data.emplace(ipc::capnp::Unserialize<CBlock>(reader.getData()));
|
|
block.data = &*data;
|
|
}
|
|
if (reader.hasUndoData()) {
|
|
undo_data.emplace(ipc::capnp::Unserialize<CBlockUndo>(reader.getUndoData()));
|
|
block.undo_data = &*undo_data;
|
|
}
|
|
mp::ReadField(mp::TypeList<interfaces::BlockInfo>(), invoke_context,
|
|
mp::Make<mp::ValueField>(reader), mp::ReadDestUpdate(block));
|
|
fn(block);
|
|
}
|
|
|
|
::capnp::Void ProxyServerMethodTraits<ipc::capnp::messages::Chain::HandleRpcParams>::invoke(
|
|
Context& context)
|
|
{
|
|
auto params = context.call_context.getParams();
|
|
auto command = params.getCommand();
|
|
|
|
CRPCCommand::Actor actor;
|
|
ReadField(TypeList<decltype(actor)>(), context, Make<ValueField>(command.getActor()), ReadDestUpdate(actor));
|
|
std::vector<std::pair<std::string, bool>> args;
|
|
ReadField(TypeList<decltype(args)>(), context, Make<ValueField>(command.getArgNames()), ReadDestUpdate(args));
|
|
|
|
auto rpc_command = std::make_unique<CRPCCommand>(command.getCategory(), command.getName(), std::move(actor),
|
|
std::move(args), command.getUniqueId());
|
|
auto handler = context.proxy_server.m_impl->handleRpc(*rpc_command);
|
|
auto results = context.call_context.getResults();
|
|
auto result = kj::heap<ProxyServer<ipc::capnp::messages::Handler>>(std::shared_ptr<interfaces::Handler>(handler.release()), *context.proxy_server.m_context.connection);
|
|
result->m_context.cleanup_fns.emplace_back([rpc_command = rpc_command.release()] { delete rpc_command; });
|
|
results.setResult(kj::mv(result));
|
|
return {};
|
|
}
|
|
|
|
void ProxyServerMethodTraits<ipc::capnp::messages::ChainClient::StartParams>::invoke(ChainContext& context)
|
|
{
|
|
// This method is never called because ChainClient::Start is overridden by
|
|
// WalletLoader::Start. The custom implementation is needed just because
|
|
// the CScheduler& argument this is supposed to pass is not serializable.
|
|
assert(0);
|
|
}
|
|
|
|
bool CustomHasValue(InvokeContext& invoke_context, const Coin& coin)
|
|
{
|
|
// Spent coins cannot be serialized due to an assert in Coin::Serialize.
|
|
return !coin.IsSpent();
|
|
}
|
|
} // namespace mp
|