// Copyright (c) 2023-present 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_COMMON_TYPES_H #define BITCOIN_IPC_CAPNP_COMMON_TYPES_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 //! Functions to serialize / deserialize common bitcoin types. 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 //! capnproto interface. Use Priority<1> so this hook has medium priority, and //! higher priority hooks could take precedence over this one. template void CustomBuildField(TypeList, 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 && std::is_same_v>> { DataStream 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 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 Unserializable && (!ipc::capnp::Deserializable) { return read_dest.update([&](auto& value) { if (!input.has()) return; auto data = input.get(); SpanReader stream({data.begin(), data.end()}); 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 void CustomBuildField(TypeList, Priority<1>, InvokeContext& invoke_context, Value&& value, Output&& output) { std::string str = value.write(); auto result = output.init(str.size()); memcpy(result.begin(), str.data(), str.size()); } template decltype(auto) CustomReadField(TypeList, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest) { return read_dest.update([&](auto& value) { auto data = input.get(); value.read(std::string_view{data.begin(), data.size()}); }); } //! Interpret empty Data fields as null CTransactionRef values. This is safe to //! do because no CTransaction is ever serialized as empty Data, and it is //! convenient because this allows std::vector to be passed as //! List(Data) even if the vector contains null values, and even though Cap'n //! Proto does not (currently) allow distinguishing between null and empty Data //! values in a List. Interpreting empty Data values as null CTransactionRef //! values works well for this purpose. template bool CustomHasField(TypeList, InvokeContext& invoke_context, const Input& input) { return input.get().size() > 0; } } // namespace mp #endif // BITCOIN_IPC_CAPNP_COMMON_TYPES_H