From 516f8637b8089ddc10bcd252a2c3db19f7561b4e Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Fri, 26 Jul 2024 08:35:33 -0400 Subject: [PATCH] Add capnp serialization code for bitcoin types - Add capnp ToBlob, ToArray, Wrap, Serialize, and Unserialize helper functions - Add support for std::chrono::seconds capnp serialization - Add support for util::Result capnp serialization --- src/ipc/capnp/common-types.h | 100 ++++++++++++++++++++++++++++++++++- src/ipc/capnp/common.capnp | 20 +++++++ 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/ipc/capnp/common-types.h b/src/ipc/capnp/common-types.h index 934dae5faf3..d507b5dd65d 100644 --- a/src/ipc/capnp/common-types.h +++ b/src/ipc/capnp/common-types.h @@ -8,9 +8,12 @@ #include #include #include +#include #include #include #include +#include +#include #include #include @@ -28,12 +31,27 @@ #include #include #include +#include #include namespace ipc { namespace capnp { +//! Convert kj::ArrayPtr to base_blob. +template +inline T ToBlob(const kj::ArrayPtr& array) +{ + return T({array.begin(), array.begin() + array.size()}); +} + +//! Convert base_blob to kj::ArrayPtr. +template +inline kj::ArrayPtr ToArray(const T& blob) +{ + return {reinterpret_cast(blob.data()), blob.size()}; +} + //! Construct a ParamStream wrapping a data stream with serialization parameters -//! needed to pass transaction objects between bitcoin processes. +//! needed to pass transaction and address 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 @@ -42,7 +60,28 @@ namespace capnp { template auto Wrap(S& s) { - return ParamsStream{s, TX_WITH_WITNESS}; + return ParamsStream{s, TX_WITH_WITNESS, CAddress::V2_NETWORK}; +} + +//! Serialize bitcoin value. +template +DataStream Serialize(const T& value) +{ + DataStream stream; + auto wrapper{Wrap(stream)}; + value.Serialize(wrapper); + return stream; +} + +//! Deserialize bitcoin value. +template +T Unserialize(const kj::ArrayPtr& data) +{ + SpanReader stream{{data.begin(), data.end()}}; + T value; + auto wrapper{Wrap(stream)}; + value.Unserialize(wrapper); + return value; } //! Detect if type has a deserialize_type constructor, which is @@ -127,6 +166,63 @@ decltype(auto) CustomReadField(TypeList, Priority<1>, InvokeContext& i }); } +//! Overload CustomBuildField and CustomReadField to serialize +//! UniValue::type_error exceptions as text strings. +template +void CustomBuildField(TypeList, Priority<1>, InvokeContext& invoke_context, + Value&& value, Output&& output) +{ + BuildField(TypeList(), invoke_context, output, std::string(value.what())); +} + +template +decltype(auto) CustomReadField(TypeList, Priority<1>, InvokeContext& invoke_context, + Input&& input, ReadDest&& read_dest) +{ + read_dest.construct(ReadField(TypeList(), invoke_context, input, mp::ReadDestTemp())); +} + +//! Overload CustomBuildField and CustomReadField to serialize util::Result +//! return values as common.capnp Result and ResultVoid structs +template +void CustomBuildField(TypeList>, Priority<1>, InvokeContext& invoke_context, Value&& value, + Output&& output) +{ + auto result = output.init(); + if (value) { + if constexpr (!std::is_same_v) { + using ValueAccessor = typename ProxyStruct::ValueAccessor; + BuildField(TypeList(), invoke_context, Make(result), *value); + } + } else { + BuildField(TypeList(), invoke_context, Make(result.initError()), + util::ErrorString(value)); + } +} + +template +decltype(auto) CustomReadField(TypeList>, Priority<1>, InvokeContext& invoke_context, + Input&& input, ReadDest&& read_dest) +{ + auto result = input.get(); + if (result.hasError()) { + bilingual_str error; + ReadField(mp::TypeList(), invoke_context, mp::Make(result.getError()), + mp::ReadDestUpdate(error)); + read_dest.construct(util::Error{std::move(error)}); + } else { + if constexpr (!std::is_same_v) { + assert (result.hasValue()); + ReadField(mp::TypeList(), invoke_context, mp::Make(result.getValue()), + mp::ReadDestEmplace( + mp::TypeList(), [&](auto&&... args) -> auto& { + return *read_dest.construct(LocalType{std::forward(args)...}); + })); + } else { + read_dest.construct(); + } + } +} } // namespace mp #endif // BITCOIN_IPC_CAPNP_COMMON_TYPES_H diff --git a/src/ipc/capnp/common.capnp b/src/ipc/capnp/common.capnp index b3359f3f07b..691fa80f4d5 100644 --- a/src/ipc/capnp/common.capnp +++ b/src/ipc/capnp/common.capnp @@ -14,3 +14,23 @@ struct BlockRef $Proxy.wrap("interfaces::BlockRef") { hash @0 :Data; height @1 :Int32; } + +struct BilingualStr $Proxy.wrap("bilingual_str") { + original @0 :Text; + translated @1 :Text; +} + +struct Result(Value) { + value @0 :Value; + error @1: BilingualStr; +} + +# Wrapper for util::Result +struct ResultVoid(Value) { + error @0: BilingualStr; +} + +struct Pair(Key, Value) { + key @0 :Key; + value @1 :Value; +}