Add capnp wrapper for Chain interface

This commit is contained in:
Russell Yanofsky 2017-12-05 15:57:12 -05:00
parent 136a73db9f
commit 6c42cf41be
9 changed files with 542 additions and 3 deletions

View file

@ -357,7 +357,7 @@ public:
//! support for writing null values to settings.json.
//! Depending on the action returned by the update function, this will either
//! update the setting in memory or write the updated settings to disk.
virtual bool updateRwSetting(const std::string& name, const SettingsUpdate& update_function) = 0;
virtual bool updateRwSetting(const std::string& name, SettingsUpdate update_function) = 0;
//! Replace a setting in <datadir>/settings.json with a new value.
//! Null can be passed to erase the setting.
@ -404,7 +404,8 @@ public:
//! Load saved state.
virtual bool load() = 0;
//! Start client execution and provide a scheduler.
//! Start client execution and provide a scheduler. (Scheduler is
//! ignored if client is out-of-process).
virtual void start(CScheduler& scheduler) = 0;
//! Save state to disk.

View file

@ -3,6 +3,7 @@
# file COPYING or https://opensource.org/license/mit/.
add_library(bitcoin_ipc STATIC EXCLUDE_FROM_ALL
capnp/chain.cpp
capnp/mining.cpp
capnp/protocol.cpp
interfaces.cpp
@ -10,6 +11,7 @@ add_library(bitcoin_ipc STATIC EXCLUDE_FROM_ALL
)
target_capnp_sources(bitcoin_ipc ${PROJECT_SOURCE_DIR}
capnp/chain.capnp
capnp/common.capnp
capnp/echo.capnp
capnp/handler.capnp

View file

@ -0,0 +1,91 @@
// 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.
#ifndef BITCOIN_IPC_CAPNP_CHAIN_TYPES_H
#define BITCOIN_IPC_CAPNP_CHAIN_TYPES_H
#include <ipc/capnp/chain.capnp.proxy.h>
#include <ipc/capnp/common.capnp.proxy-types.h>
#include <ipc/capnp/handler.capnp.proxy-types.h>
#include <interfaces/chain.h>
#include <policy/fees.h>
#include <rpc/server.h>
//! Specialization of handleRpc needed because it takes a CRPCCommand& reference
//! argument, so a manual cleanup callback is needed to free the passed
//! CRPCCommand struct and proxy ActorCallback object.
template <>
struct mp::ProxyServerMethodTraits<ipc::capnp::messages::Chain::HandleRpcParams>
{
using Context = ServerContext<ipc::capnp::messages::Chain,
ipc::capnp::messages::Chain::HandleRpcParams,
ipc::capnp::messages::Chain::HandleRpcResults>;
static ::capnp::Void invoke(Context& context);
};
//! Specialization of start method needed to provide CScheduler& reference
//! argument.
template <>
struct mp::ProxyServerMethodTraits<ipc::capnp::messages::ChainClient::StartParams>
{
using ChainContext = ServerContext<ipc::capnp::messages::ChainClient,
ipc::capnp::messages::ChainClient::StartParams,
ipc::capnp::messages::ChainClient::StartResults>;
static void invoke(ChainContext& context);
};
namespace mp {
//! Overload CustomBuildMessage, CustomPassMessage, and CustomReadMessage to
//! serialize interfaces::FoundBlock parameters. Custom conversion functions are
//! needed because there is not a 1:1 correspondence between members of the C++
//! FoundBlock class and the Cap'n Proto FoundBlockParam and FoundBlockResult
//! structs. Separate param and result structs are needed because Cap'n Proto
//! only has input and output parameters, not in/out parameters like C++.
void CustomBuildMessage(InvokeContext& invoke_context,
const interfaces::FoundBlock& dest,
ipc::capnp::messages::FoundBlockParam::Builder&& builder);
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);
void CustomReadMessage(InvokeContext& invoke_context,
const ipc::capnp::messages::FoundBlockResult::Reader& reader,
const interfaces::FoundBlock& dest);
//! Overload CustomBuildMessage and CustomPassMessage to serialize
//! interfaces::BlockInfo parameters. Custom conversion functions are needed
//! because BlockInfo structs contain pointer and reference members, and custom
//! code is needed to deal with their lifetimes. Specifics are described in the
//! implementation of these functions.
void CustomBuildMessage(InvokeContext& invoke_context,
const interfaces::BlockInfo& block,
ipc::capnp::messages::BlockInfo::Builder&& builder);
void CustomPassMessage(InvokeContext& invoke_context,
const ipc::capnp::messages::BlockInfo::Reader& reader,
::capnp::Void builder,
std::function<void(const interfaces::BlockInfo&)>&& fn);
//! CScheduler& server-side argument handling. Skips argument so it can
//! be handled by ProxyServerMethodTraits ChainClient::Start code.
template <typename Accessor, typename ServerContext, typename Fn, typename... Args>
void CustomPassField(TypeList<CScheduler&>, ServerContext& server_context, const Fn& fn, Args&&... args)
{
fn.invoke(server_context, std::forward<Args>(args)...);
}
//! CRPCCommand& server-side argument handling. Skips argument so it can
//! be handled by ProxyServerMethodTraits Chain::HandleRpc code.
template <typename Accessor, typename ServerContext, typename Fn, typename... Args>
void CustomPassField(TypeList<const CRPCCommand&>, ServerContext& server_context, const Fn& fn, Args&&... args)
{
fn.invoke(server_context, std::forward<Args>(args)...);
}
//! Override to avoid assert failures that would happen trying to serialize
//! spent coins. Probably it would be best for Coin serialization code not
//! to assert, but avoiding serialization in this case is harmless.
bool CustomHasValue(InvokeContext& invoke_context, const Coin& coin);
} // namespace mp
#endif // BITCOIN_IPC_CAPNP_CHAIN_TYPES_H

196
src/ipc/capnp/chain.capnp Normal file
View file

@ -0,0 +1,196 @@
# 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.
@0x94f21a4864bd2c65;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("ipc::capnp::messages");
using Proxy = import "/mp/proxy.capnp";
$Proxy.include("interfaces/chain.h");
$Proxy.include("rpc/server.h");
$Proxy.includeTypes("ipc/capnp/chain-types.h");
using Common = import "common.capnp";
using Handler = import "handler.capnp";
interface Chain $Proxy.wrap("interfaces::Chain") {
destroy @0 (context :Proxy.Context) -> ();
getHeight @1 (context :Proxy.Context) -> (result :Int32, hasResult :Bool);
getBlockHash @2 (context :Proxy.Context, height :Int32) -> (result :Data);
haveBlockOnDisk @3 (context :Proxy.Context, height :Int32) -> (result :Bool);
getTipLocator @4 (context :Proxy.Context) -> (result :Data);
getActiveChainLocator @5 (context :Proxy.Context, blockHash :Data) -> (result :Data);
findLocatorFork @6 (context :Proxy.Context, locator :Data) -> (result :Int32, hasResult :Bool);
hasBlockFilterIndex @7 (context :Proxy.Context, filterType :UInt8) -> (result :Bool);
blockFilterMatchesAny @8 (context :Proxy.Context, filterType :UInt8, blockHash :Data, filterSet :List(Data)) -> (result :Bool, hasResult :Bool);
findBlock @9 (context :Proxy.Context, hash :Data, block :FoundBlockParam) -> (block :FoundBlockResult, result :Bool);
findFirstBlockWithTimeAndHeight @10 (context :Proxy.Context, minTime :Int64, minHeight :Int32, block :FoundBlockParam) -> (block :FoundBlockResult, result :Bool);
findAncestorByHeight @11 (context :Proxy.Context, blockHash :Data, ancestorHeight :Int32, ancestor :FoundBlockParam) -> (ancestor :FoundBlockResult, result :Bool);
findAncestorByHash @12 (context :Proxy.Context, blockHash :Data, ancestorHash :Data, ancestor :FoundBlockParam) -> (ancestor :FoundBlockResult, result :Bool);
findCommonAncestor @13 (context :Proxy.Context, blockHash1 :Data, blockHash2 :Data, ancestor :FoundBlockParam, block1 :FoundBlockParam, block2 :FoundBlockParam) -> (ancestor :FoundBlockResult, block1 :FoundBlockResult, block2 :FoundBlockResult, result :Bool);
findCoins @14 (context :Proxy.Context, coins :List(Common.Pair(Data, Data))) -> (coins :List(Common.Pair(Data, Data)));
guessVerificationProgress @15 (context :Proxy.Context, blockHash :Data) -> (result :Float64);
hasBlocks @16 (context :Proxy.Context, blockHash :Data, minHeight :Int32, maxHeight: Int32, hasMaxHeight :Bool) -> (result :Bool);
isRBFOptIn @17 (context :Proxy.Context, tx :Data) -> (result :Int32);
isInMempool @18 (context :Proxy.Context, txid :Data) -> (result :Bool);
hasDescendantsInMempool @19 (context :Proxy.Context, txid :Data) -> (result :Bool);
broadcastTransaction @20 (context :Proxy.Context, tx: Data, maxTxFee :Int64, relay :Bool) -> (error: Text, result :Bool);
getTransactionAncestry @21 (context :Proxy.Context, txid :Data) -> (ancestors :UInt64, descendants :UInt64, ancestorsize :UInt64, ancestorfees :Int64);
calculateIndividualBumpFees @22 (context :Proxy.Context, outpoints :List(Data), targetFeerate :Data) -> (result: List(Common.PairInt64(Data)));
calculateCombinedBumpFee @23 (context :Proxy.Context, outpoints :List(Data), targetFeerate :Data) -> (result :Int64, hasResult :Bool);
getPackageLimits @24 (context :Proxy.Context) -> (ancestors :UInt64, descendants :UInt64);
checkChainLimits @25 (context :Proxy.Context, tx :Data) -> (result :Common.ResultVoid);
estimateSmartFee @26 (context :Proxy.Context, numBlocks :Int32, conservative :Bool, wantCalc :Bool) -> (calc :FeeCalculation, result :Data);
estimateMaxBlocks @27 (context :Proxy.Context) -> (result :UInt32);
mempoolMinFee @28 (context :Proxy.Context) -> (result :Data);
relayMinFee @29 (context :Proxy.Context) -> (result :Data);
relayIncrementalFee @30 (context :Proxy.Context) -> (result :Data);
relayDustFee @31 (context :Proxy.Context) -> (result :Data);
havePruned @32 (context :Proxy.Context) -> (result :Bool);
getPruneHeight @33 (context :Proxy.Context) -> (result: Int32, hasResult: Bool);
isReadyToBroadcast @34 (context :Proxy.Context) -> (result :Bool);
isInitialBlockDownload @35 (context :Proxy.Context) -> (result :Bool);
shutdownRequested @36 (context :Proxy.Context) -> (result :Bool);
initMessage @37 (context :Proxy.Context, message :Text) -> ();
initWarning @38 (context :Proxy.Context, message :Common.BilingualStr) -> ();
initError @39 (context :Proxy.Context, message :Common.BilingualStr) -> ();
showProgress @40 (context :Proxy.Context, title :Text, progress :Int32, resumePossible :Bool) -> ();
handleNotifications @41 (context :Proxy.Context, notifications :ChainNotifications) -> (result :Handler.Handler);
waitForNotificationsIfTipChanged @42 (context :Proxy.Context, oldTip :Data) -> ();
handleRpc @43 (context :Proxy.Context, command :RPCCommand) -> (result :Handler.Handler);
rpcEnableDeprecated @44 (context :Proxy.Context, method :Text) -> (result :Bool);
rpcRunLater @45 (context :Proxy.Context, name :Text, fn: RunLaterCallback, seconds: Int64) -> ();
getSetting @46 (context :Proxy.Context, name :Text) -> (result :Text);
getSettingsList @47 (context :Proxy.Context, name :Text) -> (result :List(Text));
getRwSetting @48 (context :Proxy.Context, name :Text) -> (result :Text);
updateRwSetting @49 (context :Proxy.Context, name :Text, update: SettingsUpdateCallback) -> (result :Bool);
overwriteRwSetting @50 (context :Proxy.Context, name :Text, value :Text, action :Int32) -> (result :Bool);
deleteRwSettings @51 (context :Proxy.Context, name :Text, action: Int32) -> (result :Bool);
requestMempoolTransactions @52 (context :Proxy.Context, notifications :ChainNotifications) -> ();
hasAssumedValidChain @53 (context :Proxy.Context) -> (result :Bool);
}
interface ChainNotifications $Proxy.wrap("interfaces::Chain::Notifications") {
destroy @0 (context :Proxy.Context) -> ();
transactionAddedToMempool @1 (context :Proxy.Context, tx :Data) -> ();
transactionRemovedFromMempool @2 (context :Proxy.Context, tx :Data, reason :Int32) -> ();
blockConnected @3 (context :Proxy.Context, role: UInt32, block :BlockInfo) -> ();
blockDisconnected @4 (context :Proxy.Context, block :BlockInfo) -> ();
updatedBlockTip @5 (context :Proxy.Context) -> ();
chainStateFlushed @6 (context :Proxy.Context, role: UInt32, locator :Data) -> ();
}
interface ChainClient $Proxy.wrap("interfaces::ChainClient") {
destroy @0 (context :Proxy.Context) -> ();
registerRpcs @1 (context :Proxy.Context) -> ();
verify @2 (context :Proxy.Context) -> (result :Bool);
load @3 (context :Proxy.Context) -> (result :Bool);
start @4 (context :Proxy.Context, scheduler :Void) -> ();
flush @5 (context :Proxy.Context) -> ();
stop @6 (context :Proxy.Context) -> ();
setMockTime @7 (context :Proxy.Context, time :Int64) -> ();
schedulerMockForward @8 (context :Proxy.Context, time :Int64) -> ();
}
struct FeeCalculation $Proxy.wrap("FeeCalculation") {
est @0 :EstimationResult;
reason @1 :Int32;
desiredTarget @2 :Int32;
returnedTarget @3 :Int32;
}
struct EstimationResult $Proxy.wrap("EstimationResult")
{
pass @0 :EstimatorBucket;
fail @1 :EstimatorBucket;
decay @2 :Float64;
scale @3 :UInt32;
}
struct EstimatorBucket $Proxy.wrap("EstimatorBucket")
{
start @0 :Float64;
end @1 :Float64;
withinTarget @2 :Float64;
totalConfirmed @3 :Float64;
inMempool @4 :Float64;
leftMempool @5 :Float64;
}
struct RPCCommand $Proxy.wrap("CRPCCommand") {
category @0 :Text;
name @1 :Text;
actor @2 :ActorCallback;
argNames @3 :List(RPCArg);
uniqueId @4 :Int64 $Proxy.name("unique_id");
}
struct RPCArg {
name @0 :Text;
namedOnly @1: Bool;
}
interface ActorCallback $Proxy.wrap("ProxyCallback<CRPCCommand::Actor>") {
call @0 (context :Proxy.Context, request :JSONRPCRequest, response :Text, lastCallback :Bool) -> (error :Text $Proxy.exception("std::exception"), rpcError :Text $Proxy.exception("UniValue"), typeError :Text $Proxy.exception("UniValue::type_error"), response :Text, result: Bool);
}
struct JSONRPCRequest $Proxy.wrap("JSONRPCRequest") {
id @0 :Text;
method @1 :Text $Proxy.name("strMethod");
params @2 :Text;
mode @3 :UInt32;
uri @4 :Text $Proxy.name("URI");
authUser @5 :Text;
peerAddr @6 :Text;
version @7: Int32 $Proxy.name("m_json_version");
}
interface RunLaterCallback $Proxy.wrap("ProxyCallback<std::function<void()>>") {
destroy @0 (context :Proxy.Context) -> ();
call @1 (context :Proxy.Context) -> ();
}
struct FoundBlockParam {
wantHash @0 :Bool;
wantHeight @1 :Bool;
wantTime @2 :Bool;
wantMaxTime @3 :Bool;
wantMtpTime @4 :Bool;
wantInActiveChain @5 :Bool;
wantLocator @6 :Bool;
nextBlock @7: FoundBlockParam;
wantData @8 :Bool;
}
struct FoundBlockResult {
hash @0 :Data;
height @1 :Int32;
time @2 :Int64;
maxTime @3 :Int64;
mtpTime @4 :Int64;
inActiveChain @5 :Int64;
locator @6 :Data;
nextBlock @7: FoundBlockResult;
data @8 :Data;
found @9 :Bool;
}
struct BlockInfo $Proxy.wrap("interfaces::BlockInfo") {
# Fields skipped below with Proxy.skip are pointer fields manually handled
# by CustomBuildMessage / CustomPassMessage overloads.
hash @0 :Data $Proxy.skip;
prevHash @1 :Data $Proxy.skip;
height @2 :Int32 = -1;
fileNumber @3 :Int32 = -1 $Proxy.name("file_number");
dataPos @4 :UInt32 = 0 $Proxy.name("data_pos");
data @5 :Data $Proxy.skip;
undoData @6 :Data $Proxy.skip;
chainTimeMax @7 :UInt32 = 0 $Proxy.name("chain_time_max");
}
interface SettingsUpdateCallback $Proxy.wrap("ProxyCallback<interfaces::SettingsUpdate>") {
destroy @0 (context :Proxy.Context) -> ();
call @1 (context :Proxy.Context, value :Text) -> (value :Text, result: Int32, hasResult: Bool);
}

205
src/ipc/capnp/chain.cpp Normal file
View file

@ -0,0 +1,205 @@
// 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

View file

@ -21,7 +21,10 @@
#include <mp/type-context.h>
#include <mp/type-data.h>
#include <mp/type-decay.h>
#include <mp/type-exception.h>
#include <mp/type-function.h>
#include <mp/type-interface.h>
#include <mp/type-map.h>
#include <mp/type-message.h>
#include <mp/type-number.h>
#include <mp/type-optional.h>
@ -30,6 +33,7 @@
#include <mp/type-struct.h>
#include <mp/type-threadmap.h>
#include <mp/type-vector.h>
#include <mp/type-void.h>
#include <type_traits>
#include <unordered_set>
#include <utility>
@ -223,6 +227,39 @@ decltype(auto) CustomReadField(TypeList<util::Result<LocalType>>, Priority<1>, I
}
}
}
// libmultiprocess only provides read/build functions for std::set, not
// std::unordered_set, so copy and paste those functions here.
// TODO: Move these to libmultiprocess and dedup std::set, std::unordered_set,
// and std::vector implementations.
template <typename LocalType, typename Hash, typename Input, typename ReadDest>
decltype(auto) CustomReadField(TypeList<std::unordered_set<LocalType, Hash>>, Priority<1>,
InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest)
{
return read_dest.update([&](auto& value) {
auto data = input.get();
value.clear();
for (auto item : data) {
ReadField(TypeList<LocalType>(), invoke_context, Make<ValueField>(item),
ReadDestEmplace(
TypeList<const LocalType>(), [&](auto&&... args) -> auto& {
return *value.emplace(std::forward<decltype(args)>(args)...).first;
}));
}
});
}
template <typename LocalType, typename Hash, typename Value, typename Output>
void CustomBuildField(TypeList<std::unordered_set<LocalType, Hash>>, Priority<1>, InvokeContext& invoke_context,
Value&& value, Output&& output)
{
auto list = output.init(value.size());
size_t i = 0;
for (const auto& elem : value) {
BuildField(TypeList<LocalType>(), invoke_context, ListOutput<typename decltype(list)::Builds>(list, i), elem);
++i;
}
}
} // namespace mp
#endif // BITCOIN_IPC_CAPNP_COMMON_TYPES_H

View file

@ -34,3 +34,8 @@ struct Pair(Key, Value) {
key @0 :Key;
value @1 :Value;
}
struct PairInt64(Key) {
key @0 :Key;
value @1 :Int64;
}

View file

@ -826,7 +826,7 @@ public:
return result;
}
bool updateRwSetting(const std::string& name,
const interfaces::SettingsUpdate& update_settings_func) override
interfaces::SettingsUpdate update_settings_func) override
{
std::optional<interfaces::SettingsAction> action;
args().LockSettings([&](common::Settings& settings) {

View file

@ -44,6 +44,8 @@ public:
std::string peerAddr;
std::any context;
JSONRPCVersion m_json_version = JSONRPCVersion::V1_LEGACY;
// Note: If you add fields to this struct, you should also update the
// JSONRPCRequest struct in ipc/capnp/chain.capnp.
void parse(const UniValue& valRequest);
[[nodiscard]] bool IsNotification() const { return !id.has_value() && m_json_version == JSONRPCVersion::V2; };