aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAva Chow <github@achow101.com>2024-09-25 16:39:07 -0400
committerAva Chow <github@achow101.com>2024-09-25 16:39:07 -0400
commit65f6e7078b17e6e73d74dfeb00159878099fee1e (patch)
treec89ecf5075a72279f2416ebf466719103c3700ef /src
parentda612cea032ba241e2d82418baf171b06e5f0142 (diff)
parent1a332817665f77f55090fa166930fec0e5500727 (diff)
Merge bitcoin/bitcoin#30510: multiprocess: Add IPC wrapper for Mining interface
1a332817665f77f55090fa166930fec0e5500727 doc: multiprocess documentation improvements (Ryan Ofsky) d043950ba245cdcd09dc389a71d43b0a58e41a8b multiprocess: Add serialization code for BlockValidationState (Ryan Ofsky) 33c2eee285e35db50b33efb6e782ff002f9d18ec multiprocess: Add IPC wrapper for Mining interface (Ryan Ofsky) 06882f84017f6b569b46a644f39b6d3c120ec6cf multiprocess: Add serialization code for vector<char> (Russell Yanofsky) 095286f790acda4a32f04c77aa86106007e2a0d8 multiprocess: Add serialization code for CTransaction (Russell Yanofsky) 69dfeb18761cfd933b8a9a6e69ce9d3c516ba949 multiprocess: update common-types.h to use C++20 concepts (Ryan Ofsky) 206c6e78eec7c71d317666a1af07acf357ba001e build: Make bitcoin_ipc_test depend on bitcoin_ipc (Ryan Ofsky) 070e6a32d5ff4be2f4f1f6a8200fba2f61df16e3 depends: Update libmultiprocess library for cmake headers target (Ryan Ofsky) Pull request description: Add Cap'n Proto wrapper for the Mining interface introduced in #30200, and its associated types. This PR combined with #30509 will allow a separate mining process, like the one being implemented in https://github.com/Sjors/bitcoin/pull/48, to connect to the node over IPC, and create, manage, and submit block templates. (#30437 shows another simpler demo of a process using the Mining interface.) --- This PR is part of the [process separation project](https://github.com/bitcoin/bitcoin/issues/28722). ACKs for top commit: achow101: ACK 1a332817665f77f55090fa166930fec0e5500727 TheCharlatan: ACK 1a332817665f77f55090fa166930fec0e5500727 itornaza: ACK 1a332817665f77f55090fa166930fec0e5500727 Tree-SHA512: 0791078dd6885dbd81e3d14c75fffff3da8d1277873af379ea6f9633e910c11485bb324e4cde3d936d50d343b16a10b0e8fc1e0fc6d7bdca7f522211da50c01e
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt16
-rw-r--r--src/ipc/CMakeLists.txt7
-rw-r--r--src/ipc/capnp/common-types.h139
-rw-r--r--src/ipc/capnp/common.capnp16
-rw-r--r--src/ipc/capnp/init-types.h1
-rw-r--r--src/ipc/capnp/init.capnp3
-rw-r--r--src/ipc/capnp/mining-types.h26
-rw-r--r--src/ipc/capnp/mining.capnp50
-rw-r--r--src/ipc/capnp/mining.cpp47
-rw-r--r--src/test/CMakeLists.txt8
-rw-r--r--src/test/ipc_test.capnp7
-rw-r--r--src/test/ipc_test.cpp33
-rw-r--r--src/test/ipc_test.h4
-rw-r--r--src/test/ipc_test_types.h12
14 files changed, 318 insertions, 51 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e652d43d58..36d76fbb27 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -325,6 +325,22 @@ if(WITH_MULTIPROCESS)
$<TARGET_NAME_IF_EXISTS:bitcoin_wallet>
)
list(APPEND installable_targets bitcoin-node)
+
+ if(BUILD_TESTS)
+ # bitcoin_ipc_test library target is defined here in src/CMakeLists.txt
+ # instead of src/test/CMakeLists.txt so capnp files in src/test/ are able to
+ # reference capnp files in src/ipc/capnp/ by relative path. The Cap'n Proto
+ # compiler only allows importing by relative path when the importing and
+ # imported files are underneath the same compilation source prefix, so the
+ # source prefix must be src/, not src/test/
+ add_library(bitcoin_ipc_test STATIC EXCLUDE_FROM_ALL
+ test/ipc_test.cpp
+ )
+ target_capnp_sources(bitcoin_ipc_test ${PROJECT_SOURCE_DIR}
+ test/ipc_test.capnp
+ )
+ add_dependencies(bitcoin_ipc_test bitcoin_ipc_headers)
+ endif()
endif()
diff --git a/src/ipc/CMakeLists.txt b/src/ipc/CMakeLists.txt
index 94b1ceb54e..904d72f56e 100644
--- a/src/ipc/CMakeLists.txt
+++ b/src/ipc/CMakeLists.txt
@@ -3,16 +3,21 @@
# file COPYING or https://opensource.org/license/mit/.
add_library(bitcoin_ipc STATIC EXCLUDE_FROM_ALL
+ capnp/mining.cpp
capnp/protocol.cpp
interfaces.cpp
process.cpp
)
target_capnp_sources(bitcoin_ipc ${PROJECT_SOURCE_DIR}
- capnp/echo.capnp capnp/init.capnp
+ capnp/common.capnp
+ capnp/echo.capnp
+ capnp/init.capnp
+ capnp/mining.capnp
)
target_link_libraries(bitcoin_ipc
PRIVATE
core_interface
+ univalue
)
diff --git a/src/ipc/capnp/common-types.h b/src/ipc/capnp/common-types.h
index 39e368491b..51af6a5f0a 100644
--- a/src/ipc/capnp/common-types.h
+++ b/src/ipc/capnp/common-types.h
@@ -6,6 +6,9 @@
#define BITCOIN_IPC_CAPNP_COMMON_TYPES_H
#include <clientversion.h>
+#include <interfaces/types.h>
+#include <primitives/transaction.h>
+#include <serialize.h>
#include <streams.h>
#include <univalue.h>
@@ -16,33 +19,24 @@
namespace ipc {
namespace capnp {
-//! Use SFINAE to define Serializeable<T> trait which is true if type T has a
-//! Serialize(stream) method, false otherwise.
-template <typename T>
-struct Serializable {
-private:
- template <typename C>
- static std::true_type test(decltype(std::declval<C>().Serialize(std::declval<std::nullptr_t&>()))*);
- template <typename>
- static std::false_type test(...);
-
-public:
- static constexpr bool value = decltype(test<T>(nullptr))::value;
-};
+//! Construct a ParamStream wrapping a data stream with serialization parameters
+//! needed to pass transaction objects between bitcoin processes.
+//! In the future, more params may be added here to serialize other objects that
+//! require serialization parameters. Params should just be chosen to serialize
+//! objects completely and ensure that serializing and deserializing objects
+//! with the specified parameters produces equivalent objects. It's also
+//! harmless to specify serialization parameters here that are not used.
+template <typename S>
+auto Wrap(S& s)
+{
+ return ParamsStream{s, TX_WITH_WITNESS};
+}
-//! Use SFINAE to define Unserializeable<T> trait which is true if type T has
-//! an Unserialize(stream) method, false otherwise.
+//! Detect if type has a deserialize_type constructor, which is
+//! used to deserialize types like CTransaction that can't be unserialized into
+//! existing objects because they are immutable.
template <typename T>
-struct Unserializable {
-private:
- template <typename C>
- static std::true_type test(decltype(std::declval<C>().Unserialize(std::declval<std::nullptr_t&>()))*);
- template <typename>
- static std::false_type test(...);
-
-public:
- static constexpr bool value = decltype(test<T>(nullptr))::value;
-};
+concept Deserializable = std::is_constructible_v<T, ::deserialize_type, ::DataStream&>;
} // namespace capnp
} // namespace ipc
@@ -50,42 +44,78 @@ public:
namespace mp {
//! Overload multiprocess library's CustomBuildField hook to allow any
//! serializable object to be stored in a capnproto Data field or passed to a
-//! canproto interface. Use Priority<1> so this hook has medium priority, and
+//! capnproto interface. Use Priority<1> so this hook has medium priority, and
//! higher priority hooks could take precedence over this one.
template <typename LocalType, typename Value, typename Output>
-void CustomBuildField(
- TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Value&& value, Output&& output,
- // Enable if serializeable and if LocalType is not cv or reference
- // qualified. If LocalType is cv or reference qualified, it is important to
- // fall back to lower-priority Priority<0> implementation of this function
- // that strips cv references, to prevent this CustomBuildField overload from
- // taking precedence over more narrow overloads for specific LocalTypes.
- std::enable_if_t<ipc::capnp::Serializable<LocalType>::value &&
- std::is_same_v<LocalType, std::remove_cv_t<std::remove_reference_t<LocalType>>>>* enable = nullptr)
+void CustomBuildField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Value&& value, Output&& output)
+// Enable if serializeable and if LocalType is not cv or reference qualified. If
+// LocalType is cv or reference qualified, it is important to fall back to
+// lower-priority Priority<0> implementation of this function that strips cv
+// references, to prevent this CustomBuildField overload from taking precedence
+// over more narrow overloads for specific LocalTypes.
+requires Serializable<LocalType, DataStream> && std::is_same_v<LocalType, std::remove_cv_t<std::remove_reference_t<LocalType>>>
{
DataStream stream;
- value.Serialize(stream);
+ auto wrapper{ipc::capnp::Wrap(stream)};
+ value.Serialize(wrapper);
auto result = output.init(stream.size());
memcpy(result.begin(), stream.data(), stream.size());
}
//! Overload multiprocess library's CustomReadField hook to allow any object
//! with an Unserialize method to be read from a capnproto Data field or
-//! returned from canproto interface. Use Priority<1> so this hook has medium
+//! returned from capnproto interface. Use Priority<1> so this hook has medium
//! priority, and higher priority hooks could take precedence over this one.
template <typename LocalType, typename Input, typename ReadDest>
-decltype(auto)
-CustomReadField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest,
- std::enable_if_t<ipc::capnp::Unserializable<LocalType>::value>* enable = nullptr)
+decltype(auto) CustomReadField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest)
+requires Unserializable<LocalType, DataStream> && (!ipc::capnp::Deserializable<LocalType>)
{
return read_dest.update([&](auto& value) {
if (!input.has()) return;
auto data = input.get();
SpanReader stream({data.begin(), data.end()});
- value.Unserialize(stream);
+ auto wrapper{ipc::capnp::Wrap(stream)};
+ value.Unserialize(wrapper);
});
}
+//! Overload multiprocess library's CustomReadField hook to allow any object
+//! with a deserialize constructor to be read from a capnproto Data field or
+//! returned from capnproto interface. Use Priority<1> so this hook has medium
+//! priority, and higher priority hooks could take precedence over this one.
+template <typename LocalType, typename Input, typename ReadDest>
+decltype(auto) CustomReadField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest)
+requires ipc::capnp::Deserializable<LocalType>
+{
+ assert(input.has());
+ auto data = input.get();
+ SpanReader stream({data.begin(), data.end()});
+ auto wrapper{ipc::capnp::Wrap(stream)};
+ return read_dest.construct(::deserialize, wrapper);
+}
+
+//! Overload CustomBuildField and CustomReadField to serialize std::chrono
+//! parameters and return values as numbers.
+template <class Rep, class Period, typename Value, typename Output>
+void CustomBuildField(TypeList<std::chrono::duration<Rep, Period>>, Priority<1>, InvokeContext& invoke_context, Value&& value,
+ Output&& output)
+{
+ static_assert(std::numeric_limits<decltype(output.get())>::lowest() <= std::numeric_limits<Rep>::lowest(),
+ "capnp type does not have enough range to hold lowest std::chrono::duration value");
+ static_assert(std::numeric_limits<decltype(output.get())>::max() >= std::numeric_limits<Rep>::max(),
+ "capnp type does not have enough range to hold highest std::chrono::duration value");
+ output.set(value.count());
+}
+
+template <class Rep, class Period, typename Input, typename ReadDest>
+decltype(auto) CustomReadField(TypeList<std::chrono::duration<Rep, Period>>, Priority<1>, InvokeContext& invoke_context,
+ Input&& input, ReadDest&& read_dest)
+{
+ return read_dest.construct(input.get());
+}
+
+//! Overload CustomBuildField and CustomReadField to serialize UniValue
+//! parameters and return values as JSON strings.
template <typename Value, typename Output>
void CustomBuildField(TypeList<UniValue>, Priority<1>, InvokeContext& invoke_context, Value&& value, Output&& output)
{
@@ -103,6 +133,33 @@ decltype(auto) CustomReadField(TypeList<UniValue>, Priority<1>, InvokeContext& i
value.read(std::string_view{data.begin(), data.size()});
});
}
+
+//! Generic ::capnp::Data field builder for any C++ type that can be converted
+//! to a span of bytes, like std::vector<char> or std::array<uint8_t>, or custom
+//! blob types like uint256 or PKHash with data() and size() methods pointing to
+//! bytes.
+//!
+//! Note: it might make sense to move this function into libmultiprocess, since
+//! it is fairly generic. However this would require decreasing its priority so
+//! it can be overridden, which would require more changes inside
+//! libmultiprocess to avoid conflicting with the Priority<1> CustomBuildField
+//! function it already provides for std::vector. Also, it might make sense to
+//! provide a CustomReadField counterpart to this function, which could be
+//! called to read C++ types that can be constructed from spans of bytes from
+//! ::capnp::Data fields. But so far there hasn't been a need for this.
+template <typename LocalType, typename Value, typename Output>
+void CustomBuildField(TypeList<LocalType>, Priority<2>, InvokeContext& invoke_context, Value&& value, Output&& output)
+requires
+ (std::is_same_v<decltype(output.get()), ::capnp::Data::Builder>) &&
+ (std::convertible_to<Value, std::span<const std::byte>> ||
+ std::convertible_to<Value, std::span<const char>> ||
+ std::convertible_to<Value, std::span<const unsigned char>> ||
+ std::convertible_to<Value, std::span<const signed char>>)
+{
+ auto data = std::span{value};
+ auto result = output.init(data.size());
+ memcpy(result.begin(), data.data(), data.size());
+}
} // namespace mp
#endif // BITCOIN_IPC_CAPNP_COMMON_TYPES_H
diff --git a/src/ipc/capnp/common.capnp b/src/ipc/capnp/common.capnp
new file mode 100644
index 0000000000..b3359f3f07
--- /dev/null
+++ b/src/ipc/capnp/common.capnp
@@ -0,0 +1,16 @@
+# 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.
+
+@0xcd2c6232cb484a28;
+
+using Cxx = import "/capnp/c++.capnp";
+$Cxx.namespace("ipc::capnp::messages");
+
+using Proxy = import "/mp/proxy.capnp";
+$Proxy.includeTypes("ipc/capnp/common-types.h");
+
+struct BlockRef $Proxy.wrap("interfaces::BlockRef") {
+ hash @0 :Data;
+ height @1 :Int32;
+}
diff --git a/src/ipc/capnp/init-types.h b/src/ipc/capnp/init-types.h
index 42031441b5..c3ddca27c0 100644
--- a/src/ipc/capnp/init-types.h
+++ b/src/ipc/capnp/init-types.h
@@ -6,5 +6,6 @@
#define BITCOIN_IPC_CAPNP_INIT_TYPES_H
#include <ipc/capnp/echo.capnp.proxy-types.h>
+#include <ipc/capnp/mining.capnp.proxy-types.h>
#endif // BITCOIN_IPC_CAPNP_INIT_TYPES_H
diff --git a/src/ipc/capnp/init.capnp b/src/ipc/capnp/init.capnp
index e6d358c665..1001ee5336 100644
--- a/src/ipc/capnp/init.capnp
+++ b/src/ipc/capnp/init.capnp
@@ -10,11 +10,14 @@ $Cxx.namespace("ipc::capnp::messages");
using Proxy = import "/mp/proxy.capnp";
$Proxy.include("interfaces/echo.h");
$Proxy.include("interfaces/init.h");
+$Proxy.include("interfaces/mining.h");
$Proxy.includeTypes("ipc/capnp/init-types.h");
using Echo = import "echo.capnp";
+using Mining = import "mining.capnp";
interface Init $Proxy.wrap("interfaces::Init") {
construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap);
makeEcho @1 (context :Proxy.Context) -> (result :Echo.Echo);
+ makeMining @2 (context :Proxy.Context) -> (result :Mining.Mining);
}
diff --git a/src/ipc/capnp/mining-types.h b/src/ipc/capnp/mining-types.h
new file mode 100644
index 0000000000..2e60b43fcf
--- /dev/null
+++ b/src/ipc/capnp/mining-types.h
@@ -0,0 +1,26 @@
+// 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_MINING_TYPES_H
+#define BITCOIN_IPC_CAPNP_MINING_TYPES_H
+
+#include <interfaces/mining.h>
+#include <ipc/capnp/common.capnp.proxy-types.h>
+#include <ipc/capnp/common-types.h>
+#include <ipc/capnp/mining.capnp.proxy.h>
+#include <node/miner.h>
+#include <node/types.h>
+#include <validation.h>
+
+namespace mp {
+// Custom serialization for BlockValidationState.
+void CustomBuildMessage(InvokeContext& invoke_context,
+ const BlockValidationState& src,
+ ipc::capnp::messages::BlockValidationState::Builder&& builder);
+void CustomReadMessage(InvokeContext& invoke_context,
+ const ipc::capnp::messages::BlockValidationState::Reader& reader,
+ BlockValidationState& dest);
+} // namespace mp
+
+#endif // BITCOIN_IPC_CAPNP_MINING_TYPES_H
diff --git a/src/ipc/capnp/mining.capnp b/src/ipc/capnp/mining.capnp
new file mode 100644
index 0000000000..4ea69d16c9
--- /dev/null
+++ b/src/ipc/capnp/mining.capnp
@@ -0,0 +1,50 @@
+# 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.
+
+@0xc77d03df6a41b505;
+
+using Cxx = import "/capnp/c++.capnp";
+$Cxx.namespace("ipc::capnp::messages");
+
+using Common = import "common.capnp";
+using Proxy = import "/mp/proxy.capnp";
+$Proxy.include("interfaces/mining.h");
+$Proxy.includeTypes("ipc/capnp/mining-types.h");
+
+interface Mining $Proxy.wrap("interfaces::Mining") {
+ isTestChain @0 (context :Proxy.Context) -> (result: Bool);
+ isInitialBlockDownload @1 (context :Proxy.Context) -> (result: Bool);
+ getTip @2 (context :Proxy.Context) -> (result: Common.BlockRef, hasResult: Bool);
+ waitTipChanged @3 (context :Proxy.Context, currentTip: Data, timeout: Float64) -> (result: Common.BlockRef);
+ createNewBlock @4 (scriptPubKey: Data, options: BlockCreateOptions) -> (result: BlockTemplate);
+ processNewBlock @5 (context :Proxy.Context, block: Data) -> (newBlock: Bool, result: Bool);
+ getTransactionsUpdated @6 (context :Proxy.Context) -> (result: UInt32);
+ testBlockValidity @7 (context :Proxy.Context, block: Data, checkMerkleRoot: Bool) -> (state: BlockValidationState, result: Bool);
+}
+
+interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") {
+ getBlockHeader @0 (context: Proxy.Context) -> (result: Data);
+ getBlock @1 (context: Proxy.Context) -> (result: Data);
+ getTxFees @2 (context: Proxy.Context) -> (result: List(Int64));
+ getTxSigops @3 (context: Proxy.Context) -> (result: List(Int64));
+ getCoinbaseTx @4 (context: Proxy.Context) -> (result: Data);
+ getCoinbaseCommitment @5 (context: Proxy.Context) -> (result: Data);
+ getWitnessCommitmentIndex @6 (context: Proxy.Context) -> (result: Int32);
+}
+
+struct BlockCreateOptions $Proxy.wrap("node::BlockCreateOptions") {
+ useMempool @0 :Bool $Proxy.name("use_mempool");
+ coinbaseMaxAdditionalWeight @1 :UInt64 $Proxy.name("coinbase_max_additional_weight");
+ coinbaseOutputMaxAdditionalSigops @2 :UInt64 $Proxy.name("coinbase_output_max_additional_sigops");
+}
+
+# Note: serialization of the BlockValidationState C++ type is somewhat fragile
+# and using the struct can be awkward. It would be good if testBlockValidity
+# method were changed to return validity information in a simpler format.
+struct BlockValidationState {
+ mode @0 :Int32;
+ result @1 :Int32;
+ rejectReason @2 :Text;
+ debugMessage @3 :Text;
+}
diff --git a/src/ipc/capnp/mining.cpp b/src/ipc/capnp/mining.cpp
new file mode 100644
index 0000000000..0f9533c1c7
--- /dev/null
+++ b/src/ipc/capnp/mining.cpp
@@ -0,0 +1,47 @@
+// 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 <ipc/capnp/mining-types.h>
+#include <ipc/capnp/mining.capnp.proxy-types.h>
+
+#include <mp/proxy-types.h>
+
+namespace mp {
+void CustomBuildMessage(InvokeContext& invoke_context,
+ const BlockValidationState& src,
+ ipc::capnp::messages::BlockValidationState::Builder&& builder)
+{
+ if (src.IsValid()) {
+ builder.setMode(0);
+ } else if (src.IsInvalid()) {
+ builder.setMode(1);
+ } else if (src.IsError()) {
+ builder.setMode(2);
+ } else {
+ assert(false);
+ }
+ builder.setResult(static_cast<int>(src.GetResult()));
+ builder.setRejectReason(src.GetRejectReason());
+ builder.setDebugMessage(src.GetDebugMessage());
+}
+
+void CustomReadMessage(InvokeContext& invoke_context,
+ const ipc::capnp::messages::BlockValidationState::Reader& reader,
+ BlockValidationState& dest)
+{
+ if (reader.getMode() == 0) {
+ assert(reader.getResult() == 0);
+ assert(reader.getRejectReason().size() == 0);
+ assert(reader.getDebugMessage().size() == 0);
+ } else if (reader.getMode() == 1) {
+ dest.Invalid(static_cast<BlockValidationResult>(reader.getResult()), reader.getRejectReason(), reader.getDebugMessage());
+ } else if (reader.getMode() == 2) {
+ assert(reader.getResult() == 0);
+ dest.Error(reader.getRejectReason());
+ assert(reader.getDebugMessage().size() == 0);
+ } else {
+ assert(false);
+ }
+}
+} // namespace mp
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index 2c9957117c..c23fbae92f 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -160,14 +160,6 @@ if(ENABLE_WALLET)
endif()
if(WITH_MULTIPROCESS)
- add_library(bitcoin_ipc_test STATIC EXCLUDE_FROM_ALL
- ipc_test.cpp
- )
-
- target_capnp_sources(bitcoin_ipc_test ${PROJECT_SOURCE_DIR}
- ipc_test.capnp
- )
-
target_link_libraries(bitcoin_ipc_test
PRIVATE
core_interface
diff --git a/src/test/ipc_test.capnp b/src/test/ipc_test.capnp
index 55a3dc2683..46cd08b94a 100644
--- a/src/test/ipc_test.capnp
+++ b/src/test/ipc_test.capnp
@@ -9,10 +9,15 @@ $Cxx.namespace("gen");
using Proxy = import "/mp/proxy.capnp";
$Proxy.include("test/ipc_test.h");
-$Proxy.includeTypes("ipc/capnp/common-types.h");
+$Proxy.includeTypes("test/ipc_test_types.h");
+
+using Mining = import "../ipc/capnp/mining.capnp";
interface FooInterface $Proxy.wrap("FooImplementation") {
add @0 (a :Int32, b :Int32) -> (result :Int32);
passOutPoint @1 (arg :Data) -> (result :Data);
passUniValue @2 (arg :Text) -> (result :Text);
+ passTransaction @3 (arg :Data) -> (result :Data);
+ passVectorChar @4 (arg :Data) -> (result :Data);
+ passBlockState @5 (arg :Mining.BlockValidationState) -> (result :Mining.BlockValidationState);
}
diff --git a/src/test/ipc_test.cpp b/src/test/ipc_test.cpp
index e6de6e3e47..91eba9214f 100644
--- a/src/test/ipc_test.cpp
+++ b/src/test/ipc_test.cpp
@@ -12,6 +12,7 @@
#include <test/ipc_test.capnp.proxy.h>
#include <test/ipc_test.h>
#include <tinyformat.h>
+#include <validation.h>
#include <future>
#include <thread>
@@ -88,6 +89,38 @@ void IpcPipeTest()
UniValue uni2{foo->passUniValue(uni1)};
BOOST_CHECK_EQUAL(uni1.write(), uni2.write());
+ CMutableTransaction mtx;
+ mtx.version = 2;
+ mtx.nLockTime = 3;
+ mtx.vin.emplace_back(txout1);
+ mtx.vout.emplace_back(COIN, CScript());
+ CTransactionRef tx1{MakeTransactionRef(mtx)};
+ CTransactionRef tx2{foo->passTransaction(tx1)};
+ BOOST_CHECK(*Assert(tx1) == *Assert(tx2));
+
+ std::vector<char> vec1{'H', 'e', 'l', 'l', 'o'};
+ std::vector<char> vec2{foo->passVectorChar(vec1)};
+ BOOST_CHECK_EQUAL(std::string_view(vec1.begin(), vec1.end()), std::string_view(vec2.begin(), vec2.end()));
+
+ BlockValidationState bs1;
+ bs1.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, "reject reason", "debug message");
+ BlockValidationState bs2{foo->passBlockState(bs1)};
+ BOOST_CHECK_EQUAL(bs1.IsValid(), bs2.IsValid());
+ BOOST_CHECK_EQUAL(bs1.IsError(), bs2.IsError());
+ BOOST_CHECK_EQUAL(bs1.IsInvalid(), bs2.IsInvalid());
+ BOOST_CHECK_EQUAL(static_cast<int>(bs1.GetResult()), static_cast<int>(bs2.GetResult()));
+ BOOST_CHECK_EQUAL(bs1.GetRejectReason(), bs2.GetRejectReason());
+ BOOST_CHECK_EQUAL(bs1.GetDebugMessage(), bs2.GetDebugMessage());
+
+ BlockValidationState bs3;
+ BlockValidationState bs4{foo->passBlockState(bs3)};
+ BOOST_CHECK_EQUAL(bs3.IsValid(), bs4.IsValid());
+ BOOST_CHECK_EQUAL(bs3.IsError(), bs4.IsError());
+ BOOST_CHECK_EQUAL(bs3.IsInvalid(), bs4.IsInvalid());
+ BOOST_CHECK_EQUAL(static_cast<int>(bs3.GetResult()), static_cast<int>(bs4.GetResult()));
+ BOOST_CHECK_EQUAL(bs3.GetRejectReason(), bs4.GetRejectReason());
+ BOOST_CHECK_EQUAL(bs3.GetDebugMessage(), bs4.GetDebugMessage());
+
// Test cleanup: disconnect pipe and join thread
disconnect_client();
thread.join();
diff --git a/src/test/ipc_test.h b/src/test/ipc_test.h
index 2453bfa23c..2d215a20f1 100644
--- a/src/test/ipc_test.h
+++ b/src/test/ipc_test.h
@@ -8,6 +8,7 @@
#include <primitives/transaction.h>
#include <univalue.h>
#include <util/fs.h>
+#include <validation.h>
class FooImplementation
{
@@ -15,6 +16,9 @@ public:
int add(int a, int b) { return a + b; }
COutPoint passOutPoint(COutPoint o) { return o; }
UniValue passUniValue(UniValue v) { return v; }
+ CTransactionRef passTransaction(CTransactionRef t) { return t; }
+ std::vector<char> passVectorChar(std::vector<char> v) { return v; }
+ BlockValidationState passBlockState(BlockValidationState s) { return s; }
};
void IpcPipeTest();
diff --git a/src/test/ipc_test_types.h b/src/test/ipc_test_types.h
new file mode 100644
index 0000000000..b1d4829aa7
--- /dev/null
+++ b/src/test/ipc_test_types.h
@@ -0,0 +1,12 @@
+// 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_TEST_IPC_TEST_TYPES_H
+#define BITCOIN_TEST_IPC_TEST_TYPES_H
+
+#include <ipc/capnp/common-types.h>
+#include <ipc/capnp/mining-types.h>
+#include <test/ipc_test.capnp.h>
+
+#endif // BITCOIN_TEST_IPC_TEST_TYPES_H