diff --git a/src/ipc/capnp/common-types.h b/src/ipc/capnp/common-types.h index 3e238648cfc..302da76c055 100644 --- a/src/ipc/capnp/common-types.h +++ b/src/ipc/capnp/common-types.h @@ -6,6 +6,7 @@ #define BITCOIN_IPC_CAPNP_COMMON_TYPES_H #include +#include #include #include #include @@ -17,6 +18,24 @@ namespace ipc { namespace capnp { +//! 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 +auto Wrap(S& s) +{ + return ParamsStream{s, TX_WITH_WITNESS}; +} + +//! 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 +concept Deserializable = std::is_constructible_v; } // namespace capnp } // namespace ipc @@ -36,7 +55,8 @@ void CustomBuildField(TypeList, Priority<1>, InvokeContext& invoke_co requires Serializable && std::is_same_v>> { 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()); } @@ -47,16 +67,32 @@ requires Serializable && std::is_same_v decltype(auto) CustomReadField(TypeList, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest) -requires Unserializable +requires Unserializable && (!ipc::capnp::Deserializable) { 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 +decltype(auto) CustomReadField(TypeList, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest) +requires ipc::capnp::Deserializable +{ + 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 UniValue //! parameters and return values as JSON strings. template diff --git a/src/test/ipc_test.capnp b/src/test/ipc_test.capnp index 55a3dc26839..00cfeb53215 100644 --- a/src/test/ipc_test.capnp +++ b/src/test/ipc_test.capnp @@ -15,4 +15,5 @@ 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); } diff --git a/src/test/ipc_test.cpp b/src/test/ipc_test.cpp index e6de6e3e477..59083beabc6 100644 --- a/src/test/ipc_test.cpp +++ b/src/test/ipc_test.cpp @@ -88,6 +88,15 @@ 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)); + // 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 2453bfa23c7..22fe96b6bac 100644 --- a/src/test/ipc_test.h +++ b/src/test/ipc_test.h @@ -15,6 +15,7 @@ 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; } }; void IpcPipeTest();