multiprocess: Add serialization code for CTransaction

Add support for passing CTransaction and CTransactionRef types to IPC
functions.

These types can't be passed currently because IPC serialization code currently
only supports deserializing types that have an Unserialize() method, which
CTransaction does not, because it is supposed to represent immutable
transactions. Work around this by adding a CustomReadField overload that will
call CTransaction's deserialize_type constructor.

These types also can't be passed currently because serializing transactions
requires TransactionSerParams to be set. Fix this by setting TX_WITH_WITNESS as
default serialization parameters for IPC code.
pull/30510/head
Russell Yanofsky 7 years ago
parent 69dfeb1876
commit 095286f790

@ -6,6 +6,7 @@
#define BITCOIN_IPC_CAPNP_COMMON_TYPES_H
#include <clientversion.h>
#include <primitives/transaction.h>
#include <serialize.h>
#include <streams.h>
#include <univalue.h>
@ -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 <typename S>
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 <typename T>
concept Deserializable = std::is_constructible_v<T, ::deserialize_type, ::DataStream&>;
} // namespace capnp
} // namespace ipc
@ -36,7 +55,8 @@ void CustomBuildField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_co
requires Serializable<LocalType, DataStream> && std::is_same_v<LocalType, std::remove_cv_t<std::remove_reference_t<LocalType>>>
{
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<LocalType, DataStream> && std::is_same_v<LocalType, std::r
//! priority, and higher priority hooks could take precedence over this one.
template <typename LocalType, typename Input, typename ReadDest>
decltype(auto) CustomReadField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest)
requires Unserializable<LocalType, DataStream>
requires Unserializable<LocalType, DataStream> && (!ipc::capnp::Deserializable<LocalType>)
{
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 <typename LocalType, typename Input, typename ReadDest>
decltype(auto) CustomReadField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest)
requires ipc::capnp::Deserializable<LocalType>
{
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 <typename Value, typename Output>

@ -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);
}

@ -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();

@ -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();

Loading…
Cancel
Save