common-types.h raw

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