Skip to content

Commit 095286f

Browse files
committed
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.
1 parent 69dfeb1 commit 095286f

File tree

4 files changed

+50
-3
lines changed

4 files changed

+50
-3
lines changed

src/ipc/capnp/common-types.h

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define BITCOIN_IPC_CAPNP_COMMON_TYPES_H
77

88
#include <clientversion.h>
9+
#include <primitives/transaction.h>
910
#include <serialize.h>
1011
#include <streams.h>
1112
#include <univalue.h>
@@ -17,6 +18,24 @@
1718

1819
namespace ipc {
1920
namespace capnp {
21+
//! Construct a ParamStream wrapping a data stream with serialization parameters
22+
//! needed to pass transaction objects between bitcoin processes.
23+
//! In the future, more params may be added here to serialize other objects that
24+
//! require serialization parameters. Params should just be chosen to serialize
25+
//! objects completely and ensure that serializing and deserializing objects
26+
//! with the specified parameters produces equivalent objects. It's also
27+
//! harmless to specify serialization parameters here that are not used.
28+
template <typename S>
29+
auto Wrap(S& s)
30+
{
31+
return ParamsStream{s, TX_WITH_WITNESS};
32+
}
33+
34+
//! Detect if type has a deserialize_type constructor, which is
35+
//! used to deserialize types like CTransaction that can't be unserialized into
36+
//! existing objects because they are immutable.
37+
template <typename T>
38+
concept Deserializable = std::is_constructible_v<T, ::deserialize_type, ::DataStream&>;
2039
} // namespace capnp
2140
} // namespace ipc
2241

@@ -36,7 +55,8 @@ void CustomBuildField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_co
3655
requires Serializable<LocalType, DataStream> && std::is_same_v<LocalType, std::remove_cv_t<std::remove_reference_t<LocalType>>>
3756
{
3857
DataStream stream;
39-
value.Serialize(stream);
58+
auto wrapper{ipc::capnp::Wrap(stream)};
59+
value.Serialize(wrapper);
4060
auto result = output.init(stream.size());
4161
memcpy(result.begin(), stream.data(), stream.size());
4262
}
@@ -47,16 +67,32 @@ requires Serializable<LocalType, DataStream> && std::is_same_v<LocalType, std::r
4767
//! priority, and higher priority hooks could take precedence over this one.
4868
template <typename LocalType, typename Input, typename ReadDest>
4969
decltype(auto) CustomReadField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest)
50-
requires Unserializable<LocalType, DataStream>
70+
requires Unserializable<LocalType, DataStream> && (!ipc::capnp::Deserializable<LocalType>)
5171
{
5272
return read_dest.update([&](auto& value) {
5373
if (!input.has()) return;
5474
auto data = input.get();
5575
SpanReader stream({data.begin(), data.end()});
56-
value.Unserialize(stream);
76+
auto wrapper{ipc::capnp::Wrap(stream)};
77+
value.Unserialize(wrapper);
5778
});
5879
}
5980

81+
//! Overload multiprocess library's CustomReadField hook to allow any object
82+
//! with a deserialize constructor to be read from a capnproto Data field or
83+
//! returned from capnproto interface. Use Priority<1> so this hook has medium
84+
//! priority, and higher priority hooks could take precedence over this one.
85+
template <typename LocalType, typename Input, typename ReadDest>
86+
decltype(auto) CustomReadField(TypeList<LocalType>, Priority<1>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest)
87+
requires ipc::capnp::Deserializable<LocalType>
88+
{
89+
assert(input.has());
90+
auto data = input.get();
91+
SpanReader stream({data.begin(), data.end()});
92+
auto wrapper{ipc::capnp::Wrap(stream)};
93+
return read_dest.construct(::deserialize, wrapper);
94+
}
95+
6096
//! Overload CustomBuildField and CustomReadField to serialize UniValue
6197
//! parameters and return values as JSON strings.
6298
template <typename Value, typename Output>

src/test/ipc_test.capnp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ interface FooInterface $Proxy.wrap("FooImplementation") {
1515
add @0 (a :Int32, b :Int32) -> (result :Int32);
1616
passOutPoint @1 (arg :Data) -> (result :Data);
1717
passUniValue @2 (arg :Text) -> (result :Text);
18+
passTransaction @3 (arg :Data) -> (result :Data);
1819
}

src/test/ipc_test.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ void IpcPipeTest()
8888
UniValue uni2{foo->passUniValue(uni1)};
8989
BOOST_CHECK_EQUAL(uni1.write(), uni2.write());
9090

91+
CMutableTransaction mtx;
92+
mtx.version = 2;
93+
mtx.nLockTime = 3;
94+
mtx.vin.emplace_back(txout1);
95+
mtx.vout.emplace_back(COIN, CScript());
96+
CTransactionRef tx1{MakeTransactionRef(mtx)};
97+
CTransactionRef tx2{foo->passTransaction(tx1)};
98+
BOOST_CHECK(*Assert(tx1) == *Assert(tx2));
99+
91100
// Test cleanup: disconnect pipe and join thread
92101
disconnect_client();
93102
thread.join();

src/test/ipc_test.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class FooImplementation
1515
int add(int a, int b) { return a + b; }
1616
COutPoint passOutPoint(COutPoint o) { return o; }
1717
UniValue passUniValue(UniValue v) { return v; }
18+
CTransactionRef passTransaction(CTransactionRef t) { return t; }
1819
};
1920

2021
void IpcPipeTest();

0 commit comments

Comments
 (0)