mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-22 06:52:36 +01:00
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.
This commit is contained in:
parent
69dfeb1876
commit
095286f790
4 changed files with 50 additions and 3 deletions
|
@ -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…
Add table
Reference in a new issue