Skip to content

Commit 690a1cd

Browse files
Merge dashpay#6808: fix: fix and re-enable snapshot (de)serialization tests, fix and test CQuorumRotationInfo (de)ser, fix CSimplifiedMNListDiff deser
fb23601 fix: `CSimplifiedMNListDiff` deser (UdjinM6) aac519f fix: use `extraShare` flag to guard extra share info in `CQuorumRotationInfo`, don't use `std::optional` (UdjinM6) 5dbe407 test: add serialization tests for `CQuorumRotationInfo` (UdjinM6) 24c6cf4 fix: re-enable serialization tests in `llmq_snapshot_tests` (UdjinM6) f818d13 fix: use temporary data stream in `TestSerializationRoundtrip` (UdjinM6) Pull request description: ## Issue being fixed or feature implemented Fix broken (de)serialization logic - it "works" in develop because we don't actually use it anywhere. Improve tests. ## What was done? pls see individual commits ## How Has This Been Tested? run tests ## Breaking Changes n/a ## Checklist: - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: knst: utACK fb23601 Tree-SHA512: ffba65d8621341113a3e4189981bd85ef977a916356d6b79b2676b33467e923ec00b2b559ab73d600b09e1f26adf1d796c53e743dcd7963f91f218423e670506
2 parents 4c6ce0f + fb23601 commit 690a1cd

File tree

7 files changed

+78
-70
lines changed

7 files changed

+78
-70
lines changed

src/evo/core_write.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@
197197
ssCbTxMerkleTree << cbTxMerkleTree;
198198
obj.pushKV("cbTxMerkleTree", HexStr(ssCbTxMerkleTree));
199199

200-
obj.pushKV("cbTx", EncodeHexTx(*cbTx));
200+
obj.pushKV("cbTx", EncodeHexTx(CTransaction(cbTx)));
201201

202202
UniValue deletedMNsArr(UniValue::VARR);
203203
for (const auto& h : deletedMNs) {
@@ -227,7 +227,7 @@
227227
obj.pushKV("newQuorums", newQuorumsArr);
228228

229229
// Do not assert special tx type here since this can be called prior to DIP0003 activation
230-
if (const auto opt_cbTxPayload = GetTxPayload<CCbTx>(*cbTx, /*assert_type=*/false)) {
230+
if (const auto opt_cbTxPayload = GetTxPayload<CCbTx>(cbTx, /*assert_type=*/false)) {
231231
obj.pushKV("merkleRootMNList", opt_cbTxPayload->merkleRootMNList.ToString());
232232
if (opt_cbTxPayload->nVersion >= CCbTx::Version::MERKLE_ROOT_QUORUMS) {
233233
obj.pushKV("merkleRootQuorums", opt_cbTxPayload->merkleRootQuorums.ToString());

src/evo/smldiff.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ bool BuildSimplifiedMNListDiff(CDeterministicMNManager& dmnman, const Chainstate
209209
return false;
210210
}
211211

212-
mnListDiffRet.cbTx = block.vtx[0];
212+
mnListDiffRet.cbTx = CMutableTransaction(*block.vtx[0]);
213213

214214
std::vector<uint256> vHashes;
215215
std::vector<bool> vMatch(block.vtx.size(), false);

src/evo/smldiff.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class CSimplifiedMNListDiff
4949
uint256 baseBlockHash;
5050
uint256 blockHash;
5151
CPartialMerkleTree cbTxMerkleTree;
52-
CTransactionRef cbTx;
52+
CMutableTransaction cbTx;
5353
std::vector<uint256> deletedMNs;
5454
std::vector<CSimplifiedMNListEntry> mnList;
5555
uint16_t nVersion{CURRENT_VERSION};

src/llmq/snapshot.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ UniValue CQuorumRotationInfo::ToJson() const
4545
obj.pushKV("quorumSnapshotAtHMinus2C", quorumSnapshotAtHMinus2C.ToJson());
4646
obj.pushKV("quorumSnapshotAtHMinus3C", quorumSnapshotAtHMinus3C.ToJson());
4747

48-
if (extraShare && quorumSnapshotAtHMinus4C.has_value()) {
49-
obj.pushKV("quorumSnapshotAtHMinus4C", quorumSnapshotAtHMinus4C->ToJson());
48+
if (extraShare) {
49+
obj.pushKV("quorumSnapshotAtHMinus4C", quorumSnapshotAtHMinus4C.ToJson());
5050
}
5151

5252
obj.pushKV("mnListDiffTip", mnListDiffTip.ToJson());
@@ -55,8 +55,8 @@ UniValue CQuorumRotationInfo::ToJson() const
5555
obj.pushKV("mnListDiffAtHMinus2C", mnListDiffAtHMinus2C.ToJson());
5656
obj.pushKV("mnListDiffAtHMinus3C", mnListDiffAtHMinus3C.ToJson());
5757

58-
if (extraShare && mnListDiffAtHMinus4C.has_value()) {
59-
obj.pushKV("mnListDiffAtHMinus4C", mnListDiffAtHMinus4C->ToJson());
58+
if (extraShare) {
59+
obj.pushKV("mnListDiffAtHMinus4C", mnListDiffAtHMinus4C.ToJson());
6060
}
6161
UniValue hqclists(UniValue::VARR);
6262
for (const auto& qc : lastCommitmentPerIndex) {
@@ -267,7 +267,7 @@ bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, CQuorumSnapshotMan
267267
errorRet = strprintf("Can not find quorum snapshot at H-4C");
268268
return false;
269269
} else {
270-
response.quorumSnapshotAtHMinus4C = std::move(snapshotHMinus4C);
270+
response.quorumSnapshotAtHMinus4C = std::move(snapshotHMinus4C.value());
271271
}
272272

273273
CSimplifiedMNListDiff mn4c;
@@ -283,8 +283,6 @@ bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, CQuorumSnapshotMan
283283
response.mnListDiffAtHMinus4C = std::move(mn4c);
284284
} else {
285285
response.extraShare = false;
286-
response.quorumSnapshotAtHMinus4C.reset();
287-
response.mnListDiffAtHMinus4C.reset();
288286
}
289287

290288
std::set<int> snapshotHeightsNeeded;

src/llmq/snapshot.h

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ class CQuorumRotationInfo
116116
CSimplifiedMNListDiff mnListDiffAtHMinus3C;
117117

118118
bool extraShare{false};
119-
std::optional<CQuorumSnapshot> quorumSnapshotAtHMinus4C;
120-
std::optional<CSimplifiedMNListDiff> mnListDiffAtHMinus4C;
119+
CQuorumSnapshot quorumSnapshotAtHMinus4C;
120+
CSimplifiedMNListDiff mnListDiffAtHMinus4C;
121121

122122
std::vector<llmq::CFinalCommitment> lastCommitmentPerIndex;
123123
std::vector<CQuorumSnapshot> quorumSnapshotList;
@@ -142,12 +142,9 @@ class CQuorumRotationInfo
142142
{
143143
const_cast<CQuorumRotationInfo*>(this)->SerializationOpBase(s, CSerActionSerialize());
144144

145-
if (extraShare && quorumSnapshotAtHMinus4C.has_value()) {
146-
::Serialize(s, quorumSnapshotAtHMinus4C.value());
147-
}
148-
149-
if (extraShare && mnListDiffAtHMinus4C.has_value()) {
150-
::Serialize(s, mnListDiffAtHMinus4C.value());
145+
if (extraShare) {
146+
::Serialize(s, quorumSnapshotAtHMinus4C);
147+
::Serialize(s, mnListDiffAtHMinus4C);
151148
}
152149

153150
WriteCompactSize(s, lastCommitmentPerIndex.size());
@@ -171,12 +168,9 @@ class CQuorumRotationInfo
171168
{
172169
SerializationOpBase(s, CSerActionUnserialize());
173170

174-
if (extraShare && quorumSnapshotAtHMinus4C.has_value()) {
175-
::Unserialize(s, quorumSnapshotAtHMinus4C.value());
176-
}
177-
178-
if (extraShare && mnListDiffAtHMinus4C.has_value()) {
179-
::Unserialize(s, mnListDiffAtHMinus4C.value());
171+
if (extraShare) {
172+
::Unserialize(s, quorumSnapshotAtHMinus4C);
173+
::Unserialize(s, mnListDiffAtHMinus4C);
180174
}
181175

182176
size_t cnt = ReadCompactSize(s);

src/test/llmq_snapshot_tests.cpp

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
#include <test/util/llmq_tests.h>
66
#include <test/util/setup_common.h>
77

8-
#include <llmq/snapshot.h>
98
#include <streams.h>
109
#include <univalue.h>
1110

11+
#include <llmq/params.h>
12+
#include <llmq/snapshot.h>
13+
1214
#include <boost/test/unit_test.hpp>
1315

1416
#include <vector>
@@ -116,23 +118,16 @@ BOOST_AUTO_TEST_CASE(quorum_snapshot_empty_data_test)
116118
// Test with empty data
117119
CQuorumSnapshot emptySnapshot({}, MODE_NO_SKIPPING, {});
118120

119-
// TODO: Serialization roundtrip tests are disabled because CQuorumSnapshot uses custom
120-
// serialization for bit vectors that may not produce byte-identical output after roundtrip.
121-
// These tests should be re-enabled once proper equality operators are implemented for CQuorumSnapshot.
122-
// TODO: Enable serialization roundtrip test once CQuorumSnapshot serialization is fixed
123-
// BOOST_CHECK(TestSerializationRoundtrip(emptySnapshot));
121+
// Test serialization roundtrip
122+
BOOST_CHECK(TestSerializationRoundtrip(emptySnapshot));
124123

125124
// Test with empty active members but non-empty skip list
126125
CQuorumSnapshot snapshot1({}, MODE_SKIPPING_ENTRIES, {1, 2, 3});
127-
// TODO: See above - custom bit vector serialization prevents byte-identical roundtrip
128-
// TODO: Enable serialization roundtrip test once CQuorumSnapshot serialization is fixed
129-
// BOOST_CHECK(TestSerializationRoundtrip(snapshot1));
126+
BOOST_CHECK(TestSerializationRoundtrip(snapshot1));
130127

131128
// Test with non-empty active members but empty skip list
132129
CQuorumSnapshot snapshot2({true, false, true}, MODE_NO_SKIPPING, {});
133-
// TODO: See above - custom bit vector serialization prevents byte-identical roundtrip
134-
// TODO: Enable serialization roundtrip test once CQuorumSnapshot serialization is fixed
135-
// BOOST_CHECK(TestSerializationRoundtrip(snapshot2));
130+
BOOST_CHECK(TestSerializationRoundtrip(snapshot2));
136131
}
137132

138133
BOOST_AUTO_TEST_CASE(quorum_snapshot_bit_serialization_test)
@@ -141,32 +136,24 @@ BOOST_AUTO_TEST_CASE(quorum_snapshot_bit_serialization_test)
141136

142137
// Test single bit
143138
CQuorumSnapshot snapshot1({true}, MODE_NO_SKIPPING, {});
144-
// TODO: See above - custom bit vector serialization prevents byte-identical roundtrip
145-
// TODO: Enable serialization roundtrip test once CQuorumSnapshot serialization is fixed
146-
// BOOST_CHECK(TestSerializationRoundtrip(snapshot1));
139+
BOOST_CHECK(TestSerializationRoundtrip(snapshot1));
147140

148141
// Test 8 bits (full byte)
149142
CQuorumSnapshot snapshot8(std::vector<bool>(8, true), MODE_NO_SKIPPING, {});
150-
// TODO: See above - custom bit vector serialization prevents byte-identical roundtrip
151-
// TODO: Enable serialization roundtrip test once CQuorumSnapshot serialization is fixed
152-
// BOOST_CHECK(TestSerializationRoundtrip(snapshot8));
143+
BOOST_CHECK(TestSerializationRoundtrip(snapshot8));
153144

154145
// Test 9 bits (more than one byte)
155146
CQuorumSnapshot snapshot9(std::vector<bool>(9, false), MODE_NO_SKIPPING, {});
156147
snapshot9.activeQuorumMembers[8] = true; // Set last bit
157-
// TODO: See above - custom bit vector serialization prevents byte-identical roundtrip
158-
// TODO: Enable serialization roundtrip test once CQuorumSnapshot serialization is fixed
159-
// BOOST_CHECK(TestSerializationRoundtrip(snapshot9));
148+
BOOST_CHECK(TestSerializationRoundtrip(snapshot9));
160149

161150
// Test alternating pattern
162151
std::vector<bool> alternating(16);
163152
for (size_t i = 0; i < alternating.size(); i++) {
164153
alternating[i] = (i % 2 == 0);
165154
}
166155
CQuorumSnapshot snapshotAlt(alternating, MODE_NO_SKIPPING, {});
167-
// TODO: See above - custom bit vector serialization prevents byte-identical roundtrip
168-
// TODO: Enable serialization roundtrip test once CQuorumSnapshot serialization is fixed
169-
// BOOST_CHECK(TestSerializationRoundtrip(snapshotAlt));
156+
BOOST_CHECK(TestSerializationRoundtrip(snapshotAlt));
170157
}
171158

172159
BOOST_AUTO_TEST_CASE(quorum_rotation_info_construction_test)
@@ -175,8 +162,8 @@ BOOST_AUTO_TEST_CASE(quorum_rotation_info_construction_test)
175162

176163
// Test default state
177164
BOOST_CHECK(!rotInfo.extraShare);
178-
BOOST_CHECK(!rotInfo.quorumSnapshotAtHMinus4C.has_value());
179-
BOOST_CHECK(!rotInfo.mnListDiffAtHMinus4C.has_value());
165+
BOOST_CHECK_EQUAL(::SerializeHash(rotInfo.quorumSnapshotAtHMinus4C), ::SerializeHash(CQuorumSnapshot()));
166+
BOOST_CHECK_EQUAL(::SerializeHash(rotInfo.mnListDiffAtHMinus4C), ::SerializeHash(CSimplifiedMNListDiff()));
180167
BOOST_CHECK(rotInfo.lastCommitmentPerIndex.empty());
181168
BOOST_CHECK(rotInfo.quorumSnapshotList.empty());
182169
BOOST_CHECK(rotInfo.mnListDiffList.empty());
@@ -185,7 +172,7 @@ BOOST_AUTO_TEST_CASE(quorum_rotation_info_construction_test)
185172
// Note: CQuorumRotationInfo serialization requires complex setup
186173
// This is better tested in functional tests
187174

188-
BOOST_AUTO_TEST_CASE(get_quorum_rotation_info_test)
175+
BOOST_AUTO_TEST_CASE(get_quorum_rotation_info_serialization_test)
189176
{
190177
CGetQuorumRotationInfo getInfo;
191178

@@ -194,19 +181,46 @@ BOOST_AUTO_TEST_CASE(get_quorum_rotation_info_test)
194181
getInfo.blockRequestHash = GetTestBlockHash(100);
195182
getInfo.extraShare = true;
196183

197-
// TODO: CGetQuorumRotationInfo serialization test disabled - uses standard SERIALIZE_METHODS
198-
// but may have issues with empty vectors. Should investigate and re-enable.
199-
// TODO: Enable serialization roundtrip test once CGetQuorumsBaseBlockInfo serialization is fixed
200-
// BOOST_CHECK(TestSerializationRoundtrip(getInfo));
184+
// Test serialization
185+
BOOST_CHECK(TestSerializationRoundtrip(getInfo));
201186

202187
// Test with empty base block hashes
203188
CGetQuorumRotationInfo emptyInfo;
204189
emptyInfo.blockRequestHash = GetTestBlockHash(200);
205190
emptyInfo.extraShare = false;
206191

207-
// TODO: See above - investigate serialization issues with empty base block hashes
208-
// TODO: Enable serialization roundtrip test once CGetQuorumsBaseBlockInfo serialization is fixed
209-
// BOOST_CHECK(TestSerializationRoundtrip(emptyInfo));
192+
BOOST_CHECK(TestSerializationRoundtrip(emptyInfo));
193+
}
194+
195+
BOOST_AUTO_TEST_CASE(quorum_rotation_info_serialization_test)
196+
{
197+
// Note: mnListDiff{smth} testing requires proper CSimplifiedMNListDiff setup
198+
// which is complex and better tested in functional tests
199+
200+
// Test CQuorumRotationInfo serialization with various optional field combinations
201+
CQuorumRotationInfo rotInfo;
202+
203+
// Set up basic required fields
204+
rotInfo.quorumSnapshotAtHMinusC = CQuorumSnapshot({true, false, true}, MODE_SKIPPING_ENTRIES, {1, 2});
205+
rotInfo.quorumSnapshotAtHMinus2C = CQuorumSnapshot({false, true, false}, MODE_NO_SKIPPING, {});
206+
rotInfo.quorumSnapshotAtHMinus3C = CQuorumSnapshot({true, true, false}, MODE_ALL_SKIPPED, {3});
207+
208+
// Test without extraShare
209+
rotInfo.extraShare = false;
210+
BOOST_CHECK(TestSerializationRoundtrip(rotInfo));
211+
212+
// Test with extraShare but uninitialized optional fields
213+
rotInfo.extraShare = true;
214+
BOOST_CHECK(TestSerializationRoundtrip(rotInfo));
215+
216+
// Test with extraShare and initialized snapshot
217+
rotInfo.quorumSnapshotAtHMinus4C = CQuorumSnapshot({false, false, true}, MODE_SKIPPING_ENTRIES, {4, 5, 6});
218+
BOOST_CHECK(TestSerializationRoundtrip(rotInfo));
219+
220+
CFinalCommitment commitment{GetLLMQParams(Consensus::LLMQType::LLMQ_TEST), uint256::ONE};
221+
rotInfo.lastCommitmentPerIndex.push_back(commitment);
222+
rotInfo.quorumSnapshotList.push_back(CQuorumSnapshot({false, false, true}, MODE_SKIPPING_ENTRIES, {7, 8}));
223+
BOOST_CHECK(TestSerializationRoundtrip(rotInfo));
210224
}
211225

212226
BOOST_AUTO_TEST_CASE(quorum_snapshot_json_test)

src/test/util/llmq_tests.h

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,19 @@ inline std::vector<bool> CreateBitVector(size_t size, const std::vector<size_t>&
8888
template <typename T>
8989
inline bool TestSerializationRoundtrip(const T& obj)
9090
{
91-
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
92-
ss << obj;
93-
94-
T deserialized;
95-
ss >> deserialized;
96-
97-
// Re-serialize and compare
98-
CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
99-
ss2 << deserialized;
100-
101-
return ss.str() == ss2.str();
91+
// Datastreams we will compare at the end
92+
CDataStream ss_obj(SER_NETWORK, PROTOCOL_VERSION);
93+
CDataStream ss_obj_rt(SER_NETWORK, PROTOCOL_VERSION);
94+
// A temporary datastream to perform serialization round-trip
95+
CDataStream ss_tmp(SER_NETWORK, PROTOCOL_VERSION);
96+
T obj_rt;
97+
98+
ss_obj << obj;
99+
ss_tmp << obj;
100+
ss_tmp >> obj_rt;
101+
ss_obj_rt << obj_rt;
102+
103+
return ss_obj.str() == ss_obj_rt.str();
102104
}
103105

104106
// Helper to create deterministic test data

0 commit comments

Comments
 (0)