Branch data Line data Source code
1 : : // Copyright (c) 2023-present The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #ifndef BITCOIN_IPC_CAPNP_COMMON_TYPES_H
6 : : #define BITCOIN_IPC_CAPNP_COMMON_TYPES_H
7 : :
8 : : #include <clientversion.h>
9 : : #include <interfaces/types.h>
10 : : #include <primitives/transaction.h>
11 : : #include <serialize.h>
12 : : #include <streams.h>
13 : : #include <univalue.h>
14 : :
15 : : #include <cstddef>
16 : : #include <mp/proxy-types.h>
17 : : #include <mp/type-chrono.h>
18 : : #include <mp/type-context.h>
19 : : #include <mp/type-data.h>
20 : : #include <mp/type-decay.h>
21 : : #include <mp/type-interface.h>
22 : : #include <mp/type-message.h>
23 : : #include <mp/type-number.h>
24 : : #include <mp/type-optional.h>
25 : : #include <mp/type-pointer.h>
26 : : #include <mp/type-string.h>
27 : : #include <mp/type-struct.h>
28 : : #include <mp/type-threadmap.h>
29 : : #include <mp/type-vector.h>
30 : : #include <stdexcept>
31 : : #include <type_traits>
32 : : #include <utility>
33 : :
34 : : namespace ipc {
35 : : namespace capnp {
36 : : //! Construct a ParamStream wrapping a data stream with serialization parameters
37 : : //! needed to pass transaction objects between bitcoin processes.
38 : : //! In the future, more params may be added here to serialize other objects that
39 : : //! require serialization parameters. Params should just be chosen to serialize
40 : : //! objects completely and ensure that serializing and deserializing objects
41 : : //! with the specified parameters produces equivalent objects. It's also
42 : : //! harmless to specify serialization parameters here that are not used.
43 : : template <typename S>
44 : 12 : auto Wrap(S& s)
45 : : {
46 : 12 : return ParamsStream{s, TX_WITH_WITNESS};
47 : : }
48 : :
49 : : //! Detect if type has a deserialize_type constructor, which is
50 : : //! used to deserialize types like CTransaction that can't be unserialized into
51 : : //! existing objects because they are immutable.
52 : : template <typename T>
53 : : concept Deserializable = std::is_constructible_v<T, ::deserialize_type, ::DataStream&>;
54 : : } // namespace capnp
55 : : } // namespace ipc
56 : :
57 : : //! Functions to serialize / deserialize common bitcoin types.
58 : : namespace mp {
59 : : //! Overload multiprocess library's CustomBuildField hook to allow any
60 : : //! serializable object to be stored in a capnproto Data field or passed to a
61 : : //! capnproto interface. Use Priority<1> so this hook has medium priority, and
62 : : //! higher priority hooks could take precedence over this one.
63 : : template <typename LocalType, typename Value, typename Output>
64 : 6 : void CustomBuildField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Value&& value, Output&& output)
65 : : // Enable if serializeable and if LocalType is not cv or reference qualified. If
66 : : // LocalType is cv or reference qualified, it is important to fall back to
67 : : // lower-priority Priority<0> implementation of this function that strips cv
68 : : // references, to prevent this CustomBuildField overload from taking precedence
69 : : // over more narrow overloads for specific LocalTypes.
70 : : requires Serializable<LocalType, DataStream> && std::is_same_v<LocalType, std::remove_cv_t<std::remove_reference_t<LocalType>>>
71 : : {
72 : 6 : DataStream stream;
73 [ + - ]: 6 : auto wrapper{ipc::capnp::Wrap(stream)};
74 [ # # # # ]: 6 : value.Serialize(wrapper);
[ - + ]
75 [ + - - + ]: 6 : auto result = output.init(stream.size());
76 : 6 : memcpy(result.begin(), stream.data(), stream.size());
77 : 6 : }
78 : :
79 : : //! Overload multiprocess library's CustomReadField hook to allow any object
80 : : //! with an Unserialize method to be read from a capnproto Data field or
81 : : //! returned from capnproto interface. Use Priority<1> so this hook has medium
82 : : //! priority, and higher priority hooks could take precedence over this one.
83 : : template <typename LocalType, typename Input, typename ReadDest>
84 : 2 : decltype(auto) CustomReadField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest)
85 : : requires Unserializable<LocalType, DataStream> && (!ipc::capnp::Deserializable<LocalType>)
86 : : {
87 : 2 : return read_dest.update([&](auto& value) {
88 [ # # # # : 2 : if (!input.has()) return;
# # # # #
# ][ # # #
# # # # #
# # ][ + - ]
89 : 2 : auto data = input.get();
90 : 2 : SpanReader stream({data.begin(), data.end()});
91 : 2 : auto wrapper{ipc::capnp::Wrap(stream)};
92 : 2 : value.Unserialize(wrapper);
93 : 2 : });
94 : : }
95 : :
96 : : //! Overload multiprocess library's CustomReadField hook to allow any object
97 : : //! with a deserialize constructor to be read from a capnproto Data field or
98 : : //! returned from capnproto interface. Use Priority<1> so this hook has medium
99 : : //! priority, and higher priority hooks could take precedence over this one.
100 : : template <typename LocalType, typename Input, typename ReadDest>
101 : 4 : decltype(auto) CustomReadField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest)
102 : : requires ipc::capnp::Deserializable<LocalType>
103 : : {
104 [ - + ]: 2 : assert(input.has());
105 : 4 : auto data = input.get();
106 : 4 : SpanReader stream({data.begin(), data.end()});
107 : 4 : auto wrapper{ipc::capnp::Wrap(stream)};
108 : 4 : return read_dest.construct(::deserialize, wrapper);
109 : : }
110 : :
111 : : //! Overload CustomBuildField and CustomReadField to serialize UniValue
112 : : //! parameters and return values as JSON strings.
113 : : template <typename Value, typename Output>
114 : 2 : void CustomBuildField(TypeList<UniValue>, Priority<1>, InvokeContext& invoke_context, Value&& value, Output&& output)
115 : : {
116 [ - + ]: 2 : std::string str = value.write();
117 [ + - - + ]: 4 : auto result = output.init(str.size());
118 : 2 : memcpy(result.begin(), str.data(), str.size());
119 : 2 : }
120 : :
121 : : template <typename Input, typename ReadDest>
122 : 2 : decltype(auto) CustomReadField(TypeList<UniValue>, Priority<1>, InvokeContext& invoke_context, Input&& input,
123 : : ReadDest&& read_dest)
124 : : {
125 : 4 : return read_dest.update([&](auto& value) {
126 : 2 : auto data = input.get();
127 [ - + ]: 4 : if (!value.read(std::string_view{data.begin(), data.size()})) {
128 [ # # ]: 0 : throw std::runtime_error{"invalid JSON received over IPC"};
129 : : }
130 : 2 : });
131 : : }
132 : :
133 : : //! Interpret empty Data fields as null CTransactionRef values. This is safe to
134 : : //! do because no CTransaction is ever serialized as empty Data, and it is
135 : : //! convenient because this allows std::vector<CTransactionRef> to be passed as
136 : : //! List(Data) even if the vector contains null values, and even though Cap'n
137 : : //! Proto does not (currently) allow distinguishing between null and empty Data
138 : : //! values in a List. Interpreting empty Data values as null CTransactionRef
139 : : //! values works well for this purpose.
140 : : template <typename Input>
141 : 6 : bool CustomHasField(TypeList<CTransaction>, InvokeContext& invoke_context, const Input& input)
142 : : {
143 : 6 : return input.get().size() > 0;
144 : : }
145 : : } // namespace mp
146 : :
147 : : #endif // BITCOIN_IPC_CAPNP_COMMON_TYPES_H
|